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

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

时间:2024-10-14 15:21:34浏览次数:9  
标签:public uri Uri 效果图 new import Android 监听 android

目录

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打开,就可以看到里面的数据了

标签:public,uri,Uri,效果图,new,import,Android,监听,android
From: https://blog.csdn.net/2301_76862031/article/details/142904972

相关文章

  • Android移动应用所需的工具
    基础配置Kotlin:Kotlin是一种兼具面向对象编程(OOPS)和函数式编程范式的静态类型现代编程语言。作为JVM语言,它与Java包和库完全兼容。由Google和JetBrains共同推出,Kotlin旨在成为Java的替代品,并已被Google指定为Android开发的官方语言。AndroidStudio:AndroidStudio是Android开发......
  • Android开发编译curl库给Android使用
    Android开发编译curl库给Android使用编译zlib库官网:http://zlib.net/解决后面出现的error:--with-opensslwasgivenbutOpenSSLcouldnotbedetectedexportANDROID_NDK_HOME=/home/ubuntu20/Android/Sdk/ndk/21.4.7075529exportTOOLCHAIN=$ANDROID_NDK_HOME/toolc......
  • Android中的ConstrainLayout的用法(上)
    Android中的ConstraintLayout(约束布局)是一种灵活的布局方式,它允许开发者通过定义视图之间的相对位置来创建复杂的用户界面。以下是对ConstraintLayout的详细解释:一、基本介绍ConstraintLayout是在2016年GoogleI/O大会上发布的布局方式,旨在解决复杂的页面层级嵌套过多的问题......
  • 监听下载上传进度
    在Web开发中,监听文件的上传和下载进度是提升用户体验的重要功能。通过使用现代浏览器的API,我们可以轻松实现这个功能。以下是关于如何监听上传和下载进度的详细说明。1. 监听文件上传进度对于文件上传,通常使用XMLHttpRequest(XHR)或FetchAPI。以下是如何通过这两种方法监听上......
  • Android 车载应用开发指南 - CAN Bus 协议详解
    ​在现代车载应用开发中,CAN(ControllerAreaNetwork)总线协议扮演着不可或缺的角色。作为一个汽车内部网络的标准协议,CANBus已经成为了车载系统通信的基础。而在Android车载应用开发的过程中,理解并利用好CANBus协议是必不可少的。那么,CANBus到底是什么?它又是如何在车载......
  • 包解决!Android Studio报错:Duplicate class kotlin.collections.jdk8.CollectionsJDK8K
    Executionfailedfortask':app:checkDebugDuplicateClasses'.>Afailureoccurredwhileexecutingcom.android.build.gradle.internal.tasks.CheckDuplicatesRunnable>Duplicateclasskotlin.collections.jdk8.CollectionsJDK8Ktfoundinmodul......
  • Android15车载音频之Virtualbox中QACT实时调试(八十八)
    简介:CSDN博客专家、《Android系统多媒体进阶实战》一书作者新书发布:《Android系统多媒体进阶实战》......
  • Android12.0 需求开发篇+问题解决篇之IPC socket通信
    1.需求描述        应用组C程序客户端和Android系统层Java服务端进行通信需求,这里其实在Android系统下IPC的方式有很多,像Binder作为Android特有的跨进程通信,但是应用组的同事之前是非Android系统下进行应用开发,使用的都是socket这种通用IPC通信。这里为兼容应用组代码......
  • 基于Android的家庭理财管理和实现---附源码57138
    摘 要随着经济的发展和人们生活水平的提高,家庭理财管理变得愈发重要。然而,许多家庭对于如何有效地管理个人财务以及制定预算计划感到困惑。因此,本研究旨在提供一种简单、易用且功能全面的家庭理财管理工具。本论文旨在设计和开发一款基于Android平台的家庭理财管理APP。......
  • android试题编号:2-2-8车载媒体播放 App
    (1)任务描述中控大屏仪表屏幕中间显示媒体组件,组件内上方显示正在播放的媒体信息例如“歌曲名称”“音视频名称”信息栏、“音量显示”。信息栏下方显示播放控制按钮,“播放/暂停”“上一曲下一曲”“音量滑块”“静音”,在操控设备中点击“播放/暂停”,仪表屏中的按钮随之切换“......