Android内容观察者(案例:监听数据库+代码+效果图)

news2024/11/25 16:31:39

目录

1.内容观察者概念

1. 什么是 ContentObserver?

2. 主要方法

3. 使用场景

4. 工作原理

5. 注册和注销

6. 实现步骤

7. 注意事项

2.创建内容观察者

3.注册内容观察者

4.取消注册内容观察者

5.完整的activity代码

6.案例:检测数据库

1)创建一个Android

​编辑

2)创建数据库

3)创建内容提供者

4)编写activity_main.xml文件

 5)编写MainActivity

6)修改AndroidManifest.xml文件中的内容

7)测试

8)创建第二个程序MyListener

9)创建MyObserver类

10)编写MainActivity代码

11)效果图

7.如何SQLite查看数据


1.内容观察者概念

1. 什么是 ContentObserver?

  ContentObserver 是一个抽象类,开发者需要继承这个类并实现其中的方法来定义当被观察的数据发生改变时应该采取的动作。它是用来监控内容提供者(Content Provider)所提供的数据变化的工具。

2. 主要方法

  • 构造函数ContentObserver(Handler handler) - 接收一个 Handler 对象,该对象指定了回调方法将运行在哪一个线程上。
  • onChange(boolean selfChange) (API level < 16) / onChange(boolean selfChange, Uri uri) (API level >= 16):当被观察的数据发生变化时调用此方法。从 API 级别 16 开始,提供了带有 Uri 参数的版本,可以知道具体是哪个 Uri 发生了变化。

3. 使用场景

  • 监听系统联系人的更改。
  • 监听短信的发送和接收。
  • 监听媒体库中文件的添加或删除。
  • 监听其他应用提供的数据变化。

4. 工作原理

当你注册了一个 ContentObserverContentResolver 上时,你实际上是告诉系统你对某个特定 Uri 的数据感兴趣。当那个 Uri 所代表的数据发生变化时,系统会触发 onChange() 方法,并且如果你使用的是 API 级别 16 或以上,还会传递具体的 Uri 给你的 onChange 方法。

5. 注册和注销

  • 注册:通过 ContentResolver 的 registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer) 方法来注册 ContentObservernotifyForDescendants 参数指定是否也通知子 Uri 的变化。
  • 注销:通过 unregisterContentObserver(ContentObserver observer) 方法来注销不再需要的 ContentObserver

6. 实现步骤

  1. 创建一个继承自 ContentObserver 的类。
  2. 重写 onChange() 方法来处理数据变化。
  3. 在适当的时机(如 Activity 的 onResume() 方法中),通过 ContentResolver 注册 ContentObserver
  4. 当不需要监听时(如 Activity 的 onPause() 方法中),记得注销 ContentObserver

7. 注意事项

  • onChange 方法默认是在主线程上执行的,所以不要在这个方法里执行耗时操作。
  • 如果你需要在后台线程上处理 onChange 事件,可以在创建 ContentObserver 时传入一个关联到后台线程的 Handler
  • 为了防止内存泄漏,请确保在适当的时候注销 ContentObserver

2.创建内容观察者

创建一个类继承ContentObserver类,重写构造和onChange()方法

package com.xiji.mydatabase;

import android.database.ContentObserver;
import android.os.Handler;

public class MyObserver extends ContentObserver {
    public MyObserver(Handler handler) {
        super(handler);
    }
    
    
    //数据发生改变时做的事情

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
    }
}

3.注册内容观察者

    public void registerObserver(){
        //获取观察者对象
        ContentResolver contentResolver = getContentResolver();
        //获取uri 感兴趣的uri
        Uri uri = Uri.parse("content://com.xiji.mydatabase.provider/user");
        
        //注册内容观察者
        contentResolver.registerContentObserver(uri, true, new MyObserver(new Handler()));
        
    }

4.取消注册内容观察者

ContentResolver contentResolver = getContentResolver();
        contentResolver.unregisterContentObserver(new MyObserver(new Handler()));

5.完整的activity代码

package com.xiji.mydatabase;

import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
    }

    public void registerObserver(){
        //获取观察者对象
        ContentResolver contentResolver = getContentResolver();
        //获取uri 感兴趣的uri
        Uri uri = Uri.parse("content://com.xiji.mydatabase.provider/user");

        //注册内容观察者
        contentResolver.registerContentObserver(uri, true, new MyObserver(new Handler()));

    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        ContentResolver contentResolver = getContentResolver();
        contentResolver.unregisterContentObserver(new MyObserver(new Handler()));
    }
}

6.案例:监听数据库

1)创建一个Android

2)创建数据库

MyDbHelper类

package com.xiji.myusedatabase.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyDbHelper extends SQLiteOpenHelper {
    public MyDbHelper(Context context) {
        //上下文路径    =======  数据库名
        super(context, "mydb.db", null, 1);

    }
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //创建一个数据库  user表
        sqLiteDatabase.execSQL("create table user(id integer primary key autoincrement,name varchar(20),age integer)");

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        //数据库更新操作

    }
}

3)创建内容提供者

这个主要用封装操作数据库的uri,个人感觉有点类似后端数据接口的封装,只需向指定的uri发送响应的参数就可以调用数据库了

package com.xiji.myusedatabase.db;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

import com.google.android.material.internal.ContextUtils;

public class MyContentProvider extends ContentProvider {

    //定义请求路径匹配器
    private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);;
    //成功状态码
    public static final int SUCCESS = 1;

    //操作数据库对象
    private MyDbHelper dbHelper;

    static
    {
        //数据库包路径    表名   请求码
        uriMatcher.addURI("com.xiji.myusedatabase.db", "user", SUCCESS);
    }


    public MyContentProvider() {
    }

    /**
     * 向外暴漏删除数据的uri
     *
     * **/
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        int code = uriMatcher.match(uri);
        if(code == SUCCESS)
        {
            //获取操作对象
            SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();

            //数据发生改变时需要通知的观察者
            getContext().getContentResolver().notifyChange(uri, null);
            // 执行删除  第一个参数 表名  第二个参数:条件  第三个参数:条件值
            int intRows = writableDatabase.delete("user", selection, selectionArgs);

            if(intRows > 0)
            {
                Uri deleteUri = ContentUris.withAppendedId(uri, intRows);
                //通知数据库变更
                getContext().getContentResolver().notifyChange(deleteUri, null);
            }
            return intRows;
        }else{
            throw new UnsupportedOperationException("路径不匹配删除失败");

        }


    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    /**
     * 向外暴漏插入数据库的uri
     *
     * */
    @Override
    public Uri insert(Uri uri, ContentValues values) {


        int code = uriMatcher.match(uri);
        if(code == SUCCESS)
        {
            //获取操作对象
            SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();
            // 执行插入  第一个参数:表名  第二个参数:插入的数据
            long user = writableDatabase.insert("user", null, values);
            if (user > 0) {
                Uri insertedUri = ContentUris.withAppendedId(uri, user);
                //通知数据库变更
                getContext().getContentResolver().notifyChange(insertedUri, null);
            }

            //返回插入数据的uri
            return uri;
        }else{
            throw new UnsupportedOperationException("路径不匹配插入失败");

        }

    }

    @Override
    public boolean onCreate() {

        //创建操作数据库的工具
        dbHelper = new MyDbHelper(getContext());

        return false;
    }


    /**
     *  向外暴漏查询数据库的uri  ,供其他应用操作数据库
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        // 判断请求码
        int code = uriMatcher.match(uri);

        if(code == SUCCESS)
        {
            //获取操作对象
            SQLiteDatabase readableDatabase = dbHelper.getReadableDatabase();
            // 执行查询
            // 参数1:表名  参数2:查询字段 参数3:条件  参数4:条件值  条件五:排序 返回值:游标
            return readableDatabase.query("user", projection, selection, selectionArgs, null, null, sortOrder);
        }else{
            throw new UnsupportedOperationException("路径不匹配查询失败");
        }


    }
    /**
     * 向外暴露更新数据库的uri
     *
     * */

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // 更新方法
        int code = uriMatcher.match(uri);
        if(code == SUCCESS)
        {
            //获取操作对象
            SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();
            // 执行更新  第一个参数:表名  第二个参数:更新数据  第三个参数:条件  第四个参数:条件值
            int updateRows = writableDatabase.update("user", values, selection, selectionArgs);
            if(updateRows > 0)
            {
                Uri updateUri = ContentUris.withAppendedId(uri, updateRows);
                //通知数据库变更
                getContext().getContentResolver().notifyChange(updateUri, null);
            }
            return updateRows;
        }else{
            throw new UnsupportedOperationException("路径不匹配更新失败");

        }



    }
}

4)编写activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="操作privoder"
        android:textSize="30dp"

        android:gravity="center"
        android:background="#3FFACD"
        android:padding="20dp"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加数据"
        android:id="@+id/add"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查询数据"
        android:id="@+id/query"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="删除数据"
        android:id="@+id/delete"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="更新数据"
        android:id="@+id/update"
        />
</LinearLayout>

 5)编写MainActivity

package com.xiji.myusedatabase;

import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.xiji.myusedatabase.db.MyContentProvider;
import com.xiji.myusedatabase.db.MyDbHelper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    //定义四个按钮
    private Button mBtnAdd;
    private Button mBtnQuery;
    private Button mBtnDelete;
    private Button mBtnUpdate;

    //获取信息Resolver
    private ContentResolver resolver;
    //Uri
    private Uri uri;
    //添加数据用的变量
    ContentValues values;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        //初始化控件
        initView();
        initEvent();

        initDatabase();

    }

    //控件初始化
    private void initView() {
        mBtnAdd = findViewById(R.id.add);
        mBtnQuery = findViewById(R.id.query);
        mBtnDelete = findViewById(R.id.delete);
        mBtnUpdate = findViewById(R.id.update);
    }
    //事件
    private void initEvent() {

        //获取内容提供者
        resolver = getContentResolver();
        //获取uri

        uri = Uri.parse("content://com.xiji.myusedatabase.db/user");
        //接受键值对的数据
        values = new ContentValues();


        mBtnAdd.setOnClickListener(v -> {
            //添加数据
            values.put("name", "xiji");
            values.put("age", 18);
            resolver.insert(uri, values);
            values.clear();
            Toast.makeText(MainActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
        });
        mBtnQuery.setOnClickListener(v -> {
            //查询数据
            Cursor query = resolver.query(uri, null, null, null, null);
            values.clear();
            HashMap<String, String> stringStringHashMap = new HashMap<>();
            List<HashMap> list = new ArrayList<>();
            while (query.moveToNext()) {
                @SuppressLint("Range") String name = query.getString(query.getColumnIndex("name"));
                @SuppressLint("Range") int age = query.getInt(query.getColumnIndex("age"));

                stringStringHashMap.put(name, String.valueOf(age));
                System.out.println(name + ":" + age);
                list.add(stringStringHashMap);

            }

            Toast.makeText(MainActivity.this, "查询成功"+list.toString(), Toast.LENGTH_SHORT).show();
        });
        mBtnDelete.setOnClickListener(v -> {
            resolver.delete(uri, null, null);
            values.clear();
            Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
            //删除数据
        });
        mBtnUpdate.setOnClickListener(v -> {
            values.put("age", 19);
            resolver.update(uri, values, null, null);
            values.clear();
            Toast.makeText(MainActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
            //修改数据
        });
    }

    //创建数据库
    private void initDatabase() {
        //创建数据库
        MyDbHelper myDbHelper = new MyDbHelper(this);
        SQLiteDatabase writableDatabase = myDbHelper.getWritableDatabase();

        //数据库初始化并且为数据库赋值
        resolver = getContentResolver();

        for (int i = 0; i < 10; i++) {

            values.put("name", "xiji" + i);
            values.put("age", 18 + i);
            resolver.insert(uri, values);
        }

    }
}

6)修改AndroidManifest.xml文件中的内容

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyUseDatabase"
        tools:targetApi="31">
        <provider
            android:name=".db.MyContentProvider"
            android:authorities="com.xiji.myusedatabase.db"
            android:enabled="true"
            android:exported="true"></provider>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

7)测试

启动效果

8)创建第二个程序MyListener

9)创建MyObserver类

package com.xiji.mylistener;

import android.database.ContentObserver;
import android.os.Handler;
import android.util.Log;

public class MyObserver extends ContentObserver {
    public MyObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange) {
        Log.i("监控数据库的user表变化", "数据库被人动了");
        super.onChange(selfChange);

    }


}

10)编写MainActivity代码

package com.xiji.mylistener;

import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

    private MyObserver myObserver;;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        //初始化时注册
        myObserver = new MyObserver(new Handler());
        //指向数据库user表
        Uri uri = Uri.parse("content://com.xiji.myusedatabase.db/user");


        //观察者注册
        getContentResolver().registerContentObserver(uri, true, myObserver);
    }

    @Override
    protected void onDestroy() {


        super.onDestroy();
        //观察者取消注册
        getContentResolver().unregisterContentObserver(myObserver);
    }
}

11)效果图

7.如何SQLite查看数据

1--打开右边的DeviceManger ==》三个点号   ===》Open in device Explorer

2--data===>data ==> 自己应用程序的包名 ===》databases ===>mydb.db ===>鼠标右键 ===》save as ==>选中保存路径===》保存就可以了

使用navicat打开,就可以看到里面的数据了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212085.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML(五)列表详解

在HTML中&#xff0c;列表可以分为两种&#xff0c;一种为有序列表。另一种为无序列表 今天就来详细讲解一下这两种列表如何实现&#xff0c;效果如何 1.有序列表 有序列表的标准格式如下&#xff1a; <ol><li>列表项一</li><li>列表项二</li>…

Linux下CMake入门

CMake的基础知识 什么是 CMake CMake 是一个跨平台的构建工具&#xff0c;主要用于管理构建过程。CMake 不直接构建项目&#xff0c;而是生成特定平台上的构建系统&#xff08;如 Unix 下的 Makefile&#xff0c;Windows 下的 Visual Studio 工程&#xff09;&#xff0c;然后…

《OpenCV计算机视觉》—— 人脸检测

文章目录 一、人脸检测流程介绍二、用于人脸检测的关键方法1.加载分类器&#xff08;cv2.CascadeClassifier()&#xff09;2.检测图像中的人脸&#xff08;cv2.CascadeClassifier.detectMultiscale()&#xff09; 三、代码实现 一、人脸检测流程介绍 下面是一张含有多个人脸的…

电子电气架构---软件定义汽车,产业变革

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

影刀RPA实战:操作Mysql数据库

1.摘要 影刀RPA&#xff08;Robotic Process Automation&#xff09;是一种软件自动化工具&#xff0c;它可以模拟人类用户执行各种重复性任务&#xff0c;其中包括对数据库的操作。 我们可以使用软件自动化指令&#xff0c;通过获取数据库窗口对象来操作数据库&#xff0c;也…

JSP 的 response 和 session 内置对象

文章目录 前言一、response 内置对象 1.重定向网页2.处理 HTTP 文件头3.设置输出缓存二、session 内置对象 1.创建及获取客户会话2.会话中移动指定的绑定对象3.销毁 session 内置对象4.会话超时的管理5. session 对象的应用总结 前言 JSP 的 response 和 session 内置对像&…

linux tar 打包文件去掉文件所在路径

一、准备目录 /root/tmp/images /root/tmp/images2 执行命令打包目录/root/tmp/images 到 /root/tmp/images.tar.gz 再解压到/root/tmp/images2 cd /root/tmp/images && tar -cvzf images.tar.gz * && mv images.tar.gz /root/tmp/ tar -C /root/tmp/image…

ctf.bugku-baby lfi 2

题目来源&#xff1a;baby lfi 2 - Bugku CTF平台 访问页面 翻译解析&#xff1a;百度翻译-您的超级翻译伙伴&#xff08;文本、文档翻译&#xff09; (baidu.com) LFI Warmups- level 2 -本地文件包含&#xff08;Local File Inclusion&#xff0c;简称LFI&#xff09; Hello…

力扣面试150 交错字符串 二维DP

Problem: 97. 交错字符串 &#x1f468;‍&#x1f3eb; 参考题解 class Solution {public boolean isInterleave(String s1, String s2, String s3) {int m s1.length();int n s2.length();if(s3.length() ! m n) return false;boolean[][] dp new boolean[m1][n1];dp[0]…

ZYNQ使用XGPIO驱动外设模块(后半部分)

目录 注意重点&#xff1a; 一、SDK代码开发部分&#xff1a; 显示所需的字符编码&#xff1a; 1.用于显示8x16的字符函数&#xff1a; 2.绘制图片: 3.清楚给定两个坐标之间的显示&#xff1a; 4.显示16*32的阿拉伯数字字符&#xff1a; 5.显示16*32的整型数字&#xff…

Excel:vlookup函数实现查找

1.要查找宋江的英语&#xff0c;把鼠标放在对应单元格然后开始编辑 2.选中所选区域&#xff0c;点击F4锁定区域&#xff0c;不然下拉填充的时候会变VLOOKUP 在查找时有严格要求&#xff0c;查找值必须在所选区域的第一列&#xff0c;因此如果你的查找值不在第一列&#xff0c;可…

TGRS 2024 面向雾天遥感图像的定向目标检测算法

TGRS 2024 | 面向雾天遥感图像的定向目标检测算法 论文信息 摘要 目前&#xff0c;大量工作集中在航空目标检测上&#xff0c;并取得了良好的结果。尽管这些方法在传统数据集上取得了有希望的结果&#xff0c;但在恶劣天气条件下捕获的低质量图像中定位对象仍然具有挑战性。目…

RabbitMQ 入门(四)SpringAMQP五种消息类型

一、WorkQueue(工作消息队列) Work queues&#xff0c;也被称为&#xff08;Task queues&#xff09;&#xff0c;任务模型。简单来说就是让多个消费者绑定到一个队列&#xff0c;共同消费队列中的消息。 当消息处理比较耗时的时候&#xff0c;可能生产消息的速度会远远大于…

【亲测可行】ubuntu下载安装c++版opencv4.7.0和4.5.0 安装opencv4.5.0报错及解决方法

文章目录 &#x1f315;系统配置&#x1f315;打开终端&#xff0c;退出anacoda激活环境(如果有的话)&#x1f315;安装依赖&#x1f319;安装g, cmake, make, wget, unzip&#xff0c;若已安装&#xff0c;此步跳过&#x1f319;安装opencv依赖的库&#x1f319;安装可选依赖 …

关于md5强比较和弱比较绕过的实验

在ctf比赛题中我们的md5强弱比较的绕过题型很多&#xff0c;大部分都是结合了PHP来进行一个考核。这一篇文章我将讲解一下最基础的绕过知识。 MD5弱比较 比较的步骤 在进行弱比较时&#xff0c;PHP会按照以下步骤执行&#xff1a; 确定数据类型&#xff1a;检查参与比较的两…

要笑死了java

有一天&#xff0c;Java开发者Jack对他的朋友说&#xff1a;“最近我总感觉自己被逼得喘不过气。” 朋友问&#xff1a;“为什么啊&#xff1f;” Jack叹了口气道&#xff1a;“因为我一直在try里&#xff0c;但从来没有catch到过break&#xff01;” 为什么程序员总是分不清圣…

【Ubuntu】“Linux版PhotoShop”绘图软件的安装和汉化

【Ubuntu】“Linux版PhotoShop”绘图软件的安装和汉化 零、前言 最近换了Linux系统&#xff0c;但是写教程做PPT的时候还是得用到绘图软件&#xff0c;上网一查&#xff0c;总结对比之后发现Krita比较好用&#xff0c;故此讲解一下如何安装和汉化Krita。 壹、安装 安装很简…

spring 如何将mutipartFile转存到本地磁盘

两者的区别和联系 MutipartFile是spring的一部分&#xff0c;File则是java的标准类MutipartFile用于接收web传递的文件&#xff0c;File操作本地系统的文件 MutipartFile 转换File的三种方式 使用MutipartFile 自带的transferTo方法使用java自带的FileOutPutStream流使用java自…

AirServer v7.2.7 破解版 – iPhone屏幕镜像工具

AirServer v7.2.7 破解版 – iPhone屏幕镜像工具可以将你的Mac转变为一个通用的镜像接收器&#xff0c;除了无法接收Miracast外&#xff0c;你可以使用内置的AirPlay或Google Cast基于屏幕投影功能来镜像你的设备屏幕&#xff1b;一次一个或同时投影到AirServer。用户可以从任何…

基于stm32的HAL库的串口实现不定长中断(空闲中断)(四)

串口实现不定长中断&#xff08;空闲中断&#xff09; 目录 串口实现不定长中断&#xff08;空闲中断&#xff09; 1、串口头文件 2、串口初始化配置 3、msp公用的一个函数这里有三个串口&#xff0c;都会调用msp先进行一个判断 4、对接收数组进行清空 5、中断服务函数--…