• ContentProvider与ContentResolver


    一、ContentProvider

    1.定义:

    ContentProvider相当于一个接口,该接口的作用是对外暴露本APP的数据。
    当我们想允许本应用的数据可以被别的应用进行读取,可以让本的APP实现ContentProvider类,同时注册一个URI,然后其他应用只要使用ContentResolver访问指定URI就可以操作我们APP里的数据了。

    2.使用步骤:

    ContentProvider为数据定义一个URI,其他Application想要操作本Application中的数据时,只需要获得一个ContentResolver对象并传入相应的URI,即可操作本Application中 URI 下的数据。

    请添加图片描述

    第一步.创建类继承ContentProvider:

    需要重写onCreate( )、delete( )、getType( )、insert( )、query( )、update( )方法。
    其中增删查改方法可以用来操作本应用的SQLite数据库

    package com.example.myapplication;
    
    import android.content.ContentProvider;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    
    public class MyContentProvider extends ContentProvider{
        /*创建ContentProvider时调用,其他应用程序第一次访问ContentProvider时,该ContentProvider会被创建出来,立即回调该方法
         */
        @Override
        public boolean onCreate() {
            return false;
    
        }
        /*根据ContentProvider传来的属性字段查询数据库中指定条件下的数据
                projection:要查询的列名
                selection:where子句的内容
                selectionArgs:selection中对应的参数
                sortOrder:排序规则
         */
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
            return null;
        }
        /*用于返回指定URI代表的数据的MIME类型
         */
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            return null;
        }
        /*根据ContentProvider传来的属性字段插入contentValues中的的数据到数据库
         */
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
            return null;
        }
        /*根据ContentProvider传来的属性字段删除数据库中where条件匹配的数据信息
         */
        @Override
        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }
        /*根据ContentProvider传来的属性字段更新更新where条件匹配的数据为contentValues
         */
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
            return 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    第二步:注册ContentProvider:

    
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapplication">
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/Theme.MyApplication">
            <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>
            
            
            <provider
                android:name="com.example.myapplication.MyContentProvider"
                android:authorities="uri.mycontentprovider"
                android:exported="true"/>
        application>
    manifest>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    二、URI:

    请添加图片描述

    每个对外暴露数据的Application都必须有一个URI,其他Application通过该URI访问本应用的数据。

    1.常用方法:

    //字符串转URI
    Uri uri = Uri.parse("uri.mycontentprovider");
    //获取uri中数据部分存入一个List中
    List<String> segments = uri.getPathSegments();
    
    • 1
    • 2
    • 3
    • 4

    2.解析URI:

    请添加图片描述

    不同URI代表着不同的操作,所以当ContentProvider接收到URI时,我们需要解析URI才能知道该条URI想进行什么操作。

    (1)UriMatcher解析URI:

    定义:

    UriMatcher类用于匹配Uri。当应用程序通过URI访问ContentProvider时,UriMatcher用于检查URI是否符合我们预先定义好的若干条规则,如果符合某条规则,则可使用该URI,返回该条规则的匹配码。

    使用步骤:
    第一步:创建UriMatcher
    UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);//常量UriMatcher.NO_MATCH表示Uri不匹配时的返回码
    
    • 1
    第二步:设置URI匹配规则

    ContentProvider接收到外部发来的请求URI后会根据匹配规则进行匹配。

    matcher.addURI("uri.mycontentprovider", "studentall", 1);//查询所有学生记录,如果URI为:uri.mycontentprovider/studentall时会返回1
    matcher.addURI("uri.mycontentprovider", "students/*", 2);//根据学号和姓名模糊查询获得多个记录。*是字符通配符,可以匹配任何字符,例如:将来可以匹配students/stuId="+stuId+"&stuName="+stuName
    matcher.addURI("uri.mycontentprovider", "student/#", 3);//单个学生,需要根据studentid来判断,#是数字通配符,可以匹配任何数字,例如:将来可以匹配student/001,student/002等
    matcher.addURI("uri.mycontentprovider", "insertStudent", 4);//插入学生
    matcher.addURI("uri.mycontentprovider", "deleteStudent", 5);//删除单个学生,需要根据studentid来判断,#将来调用时实参使用学号来代替
    matcher.addURI("uri.mycontentprovider", "updateStudent", 6);//修改学生信息
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    第三步:在需要匹配的地方调用match()方法检查是否匹配
    //点击”查询全部学生“按钮时,ContentResolver会执行query方法,发送URI,ContentProvider捕获到该URI后需要判断匹配规则是否符合,符合才执行查询操作
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (matcher.match(Uri.parse("uri.mycontentprovider/studentall"))){
                //URI符合我们设置的匹配规则
                case 1:{
                    //查询操作
                }
                //URI不匹配
                default:{
                    break;
                }
            }    
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (2)ContentUris解析URI:

    ContentUris类用于操作Uri路径后面的ID(唯一标识符)部分。

    /*
    public static Uri withAppendedId (Uri contentUri, long id):用于为路径加上ID部分。
    */
    Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person");
    Uri resultUri = ContentUris.withAppendedId(uri, 10);
    //生成后的Uri为:content://cn.itcast.provider.personprovider/person/10
    
    /*
    public static long parseId (Uri contentUri)该方法用于从路径中获取ID部分。
    */
    Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10")
    long personid = ContentUris.parseId(uri);//获取的结果为:10
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三、ContentResolver:

    1.定义:

    当外部应用需要对ContentProvider中的数据进行增删改查操作时,可以使用ContentResolver类完成。

    2.使用步骤:

    第一步:获取ContentResolver对象

    通过Activity的静态方法获取ContentResolver对象。

    ContentResolver resolver = this.getContentResolver();
    
    • 1

    第二步:使用ContentResolver对象进行增删查改

    ContentResolver 类提供了与ContentProvider 类相同签名的四个方法,通过给这些方法提供ContentProvider的URI使得这些方法能够调用ContenProvider中与之对应的CRUD方法,并得到返回的结果。
    请添加图片描述

    ContentResolver resolver = this.getContentResolver();
    /*根据传入的URI查询指定条件下的数据
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    */
    resolver.query(ContentProviderURI,SQL参数...);
    /*根据传入的URI插入contentValues对应的数据
    public Uri insert(Uri uri, ContentValues values)
    */
    resolver.insert(ContentProviderURI,SQL参数...);
    /*根据传入的URI删除where条件匹配的所有数据
    public int delete(Uri uri, String selection, String[] selectionArgs)
    */
    resolver.delete(ContentProviderURI,SQL参数...);
    /*根据传入的URI更新更新where条件匹配的数据为contentValues
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
    */
    resolver.update(ContentProviderURI,SQL参数...);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    四、ContentObserve监听器

    1.定义:

    用于监听ContentProvider中单个URI下数据的变化。

    2.使用步骤:

    第一步:设置ContentProvider发送通知

    在ContentProvider 能使数据发生变化的方法中 调用以下方法:

    //该方法的作用是:通知注册在该URI:content://uri.mycontentprovider/studentall上的监听器:该URI上的数据发生变化
    this.getContext().getContentResolver().notifyChange(Uri.parse("content://uri.mycontentprovider/studentall"), null);        
    
    • 1
    • 2

    第二步:创建类并继承ContentObserver类

    需要重写构造方法和onChange方法。

    class MyContentObserve extends ContentObserver{
        /*
        当观察到Uri代表的数据发生变化时,会触发该方法
         */
        @Override
        public void onChange(boolean selfChange, @Nullable Uri uri) {
    		//消息弹框,通知所有注册了监听的Application该URI下的数据发生变化
            Toast.makeText(MainActivity.this,"来自ContentObserver的通知:ContentProvider的数据发生改变",Toast.LENGTH_LONG).show();
        }
    
        public MyContentObserve(Handler handler) {
            super(handler);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    第三步:ContentResolver中开启监听:

    在需要进行监听的位置加入下面的代码,当该URI下的数据发生变化时,ContentProvider会发送通知,然后就会被下面的代码接收到通知,接收到通知后会调用MyContentObserve的onChange方法

    //获取ContentResolver
    ContentResolver resolver = this.getContentResolver();
    //注册ContentObserver的监听器,当uri="content://uri.mycontentprovider/studentall"的数据有变化时,就触发,回调MyObserver对象的onChange(boolean selfChange, Uri uri)方法
    contentResolver.registerContentObserver(Uri.parse("content://uri.mycontentprovider/studentall"),true,new MyContentObserve(new Handler()));
    
    • 1
    • 2
    • 3
    • 4

    五、案例:对外部Application的数据库进行CRUD:

    1.项目结构:

    数据库位于app中,app2访问app的数据库进行增删查改。
    在这里插入图片描述

    2.app:

    (1)ContentProvider:
    package com.example.myapplication;
    
    import android.content.ContentProvider;
    import android.content.ContentValues;
    import android.content.UriMatcher;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.net.Uri;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    
    import java.util.List;
    
    public class MyContentProvider extends ContentProvider{
        //数据库对象,MyContentProvider是用来对外暴露数据库数据的
        private MySQLiteOpenHelper openHelper;
        //URI解析器
        private UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    
        /*创建ContentProvider时调用,其他应用程序第一次访问ContentProvider时,该ContentProvider会被创建出来,立即回调该方法
         */
        @Override
        public boolean onCreate() {
            //获得数据库dbHelper对象,将来用它得到SQLLiteDatabase对象
            openHelper = new MySQLiteOpenHelper(this.getContext());
            //设置该ContentProvider下的URI匹配规则
            //当ContentResolver发送的请求为content://uri.mycontentprovider/studentall时,会执行匹配码1对应的方法
            matcher.addURI("uri.mycontentprovider","studentall",1);
            //当ContentResolver发送的请求为content://uri.mycontentprovider/student/......时,会执行匹配码2对应的方法
            matcher.addURI("uri.mycontentprovider","student/#",2);
            //当ContentResolver发送的请求为content://uri.mycontentprovider/insertStudent时,会执行匹配码3对应的方法
            matcher.addURI("uri.mycontentprovider","insertStudent",3);
            //当ContentResolver发送的请求为content://uri.mycontentprovider/deleteStudent时,会执行匹配码4对应的方法
            matcher.addURI("uri.mycontentprovider","deleteStudent",4);
            //当ContentResolver发送的请求为content://uri.mycontentprovider/updateStudent时,会执行匹配码5对应的方法
            matcher.addURI("uri.mycontentprovider","updateStudent",5);
            return true;
        }
        /*根据ContentProvider传来的属性字段查询数据库中指定条件下的数据
                projection:要查询的列名
                selection:where子句的内容
                selectionArgs:selection中对应的参数
                sortOrder:排序规则
         */
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
            SQLiteDatabase database = openHelper.getWritableDatabase();
            Cursor cursor = null;
            //根据ContentResolver传来的URI匹配相应的匹配码,并执行匹配码下的方法
            switch (matcher.match(uri)){
                //匹配码1对应的方法
                case 1:{
                    //操作本ContentProvider的数据库,根据ContentResolver传来的数据进行查询操作
                    cursor = database.query("student", projection, selection, selectionArgs, "", "", sortOrder);
                    break;
                }
                //匹配码2对应的方法
                case 2:{
                    //操作本ContentProvider的数据库,根据ContentResolver传来的数据进行查询操作
                    cursor = database.query("student", projection, selection, selectionArgs, "", "", sortOrder);
                    break;
                }
                //不匹配时执行的默认方法,即ContentResolver想执行query方法但是匹配码对应不上,也就是请求的URI在ContentProvider中没有相应的匹配码
                default:{
                    break;
                }
            }
            //database.close();
            //返回结果给方法调用者ContentResolver
            return cursor;
        }
        /*用于返回指定URI代表的数据的MIME类型
         */
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            return null;
        }
        /*根据ContentProvider传来的属性字段插入contentValues中的的数据到数据库
         */
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
            SQLiteDatabase database = openHelper.getWritableDatabase();
            //根据ContentResolver传来的URI匹配相应的匹配码,并执行匹配码下的方法
            switch (matcher.match(uri)){
                //匹配码3对应的方法
                case 3:{
                    //操作本ContentProvider的数据库,根据ContentResolver传来的数据执行插入操作
                    database.insert("student",null,contentValues);
                    //发送通知给所有注册了监听的ContentResolver,告诉它们数据库中的信息发生改变
                    this.getContext().getContentResolver().notifyChange(Uri.parse("content://uri.mycontentprovider/studentall"), null);
                    break;
                }
                //不匹配时执行的默认方法,即ContentResolver想执行query方法但是匹配码对应不上,也就是请求的URI在ContentProvider中没有相应的匹配码
                default:{
                    break;
                }
            }
            //database.close();
            //返回结果给方法调用者ContentResolver
            return uri;
        }
        /*根据ContentProvider传来的属性字段删除数据库中where条件匹配的数据信息
         */
        @Override
        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
            SQLiteDatabase database = openHelper.getWritableDatabase();
            int i =0;
            //根据ContentResolver传来的URI匹配相应的匹配码,并执行匹配码下的方法
            switch (matcher.match(uri)){
                //匹配码4对应的方法
                case 4:{
                    //操作本ContentProvider的数据库,根据ContentResolver传来的数据执行删除操作
                    i = database.delete("student", selection, selectionArgs);
                    //发送通知给所有注册了监听的ContentResolver,告诉它们数据库中的信息发生改变
                    this.getContext().getContentResolver().notifyChange(Uri.parse("content://uri.mycontentprovider/studentall"), null);
                    break;
                }
                //不匹配时执行的默认方法,即ContentResolver想执行query方法但是匹配码对应不上,也就是请求的URI在ContentProvider中没有相应的匹配码
                default:{
                    break;
                }
            }
            //database.close();
            //返回结果给方法调用者ContentResolver
            return i;
        }
        /*根据ContentProvider传来的属性字段更新更新where条件匹配的数据为contentValues
         */
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) {
            SQLiteDatabase database = openHelper.getWritableDatabase();
            int i = 0;
            //根据ContentResolver传来的URI匹配相应的匹配码,并执行匹配码下的方法
            switch (matcher.match(uri)){
                //匹配码5对应的方法
                case 5:{
                    //操作本ContentProvider的数据库,根据ContentResolver传来的数据执行更新操作
                    i = database.update("student",contentValues, selection, selectionArgs);
                    //发送通知给所有注册了监听的ContentResolver,告诉它们数据库中的信息发生改变
                    this.getContext().getContentResolver().notifyChange(Uri.parse("content://uri.mycontentprovider/studentall"), null);
                    break;
                }
                //不匹配时执行的默认方法,即ContentResolver想执行query方法但是匹配码对应不上,也就是请求的URI在ContentProvider中没有相应的匹配码
                default:{
                    break;
                }
            }
            //database.close();
            //返回结果给方法调用者ContentResolver
            return i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    (2)配置文件:
    
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapplication">
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/Theme.MyApplication">
            <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>
            
            
            <provider
                android:name="com.example.myapplication.MyContentProvider"
                android:authorities="uri.mycontentprovider"
                android:exported="true"/>
        application>
    manifest>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    (3)数据库创建类:
    package com.example.myapplication;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    import androidx.annotation.Nullable;
    
    public class MySQLiteOpenHelper extends SQLiteOpenHelper {
        //创建数据库mydatabase
        public MySQLiteOpenHelper(@Nullable Context context) {
            super(context, "mydatabase", null, 1);
        }
        //建表student
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            sqLiteDatabase.execSQL("create table student(stuId varchar(20) primary key ,stuName varchar(20) ,stuSex varchar(20) ,stuAge varchar(20))");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.app2:

    (1)ContentResolver:
    package com.example.app2;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.database.ContentObserver;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Handler;
    import android.util.Log;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.SimpleCursorAdapter;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //获取ContentResolver
            ContentResolver resolver = this.getContentResolver();
            //获取控件对象
            EditText stuId = findViewById(R.id.stuId);
            EditText stuName = findViewById(R.id.stuName);
            EditText stuSex = findViewById(R.id.stuSex);
            EditText stuAge = findViewById(R.id.stuAge);
            ListView listview = findViewById(R.id.listview);
            Button addBtn = findViewById(R.id.addbtn);
            Button deleteBtn = findViewById(R.id.deletebtn);
            Button selectBtn = findViewById(R.id.selectbtn);
            Button updateBtn = findViewById(R.id.updatebtn);
    
            //注册ContentObserver的监听器,当uri="content://uri.mycontentprovider/studentall"的数据有变化时,就触发,在MyObserver对象的onChang()方法
            resolver.registerContentObserver(Uri.parse("content://uri.mycontentprovider/studentall"),true,new MyObserver(new Handler()));
    
            //点击按钮插入数据
            addBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ContentValues values = new ContentValues();
                    values.put("stuId",stuId.getText().toString());
                    values.put("stuName",stuName.getText().toString());
                    values.put("stuSex",stuSex.getText().toString());
                    values.put("stuAge",stuAge.getText().toString());
                    //发送请求,调用resolver的insert方法并传入URI,管理该URI的ContentProvider捕获到该请求后会执行对应的insert方法,返回结果给该请求(相当于该方法调用了ContentProvider中的inserrt方法)
                    resolver.insert(Uri.parse("content://uri.mycontentprovider/insertStudent"), values);
                    //消息弹框
                    Toast.makeText(MainActivity.this,"远程插入成功",Toast.LENGTH_LONG).show();
                    //将输入框数据置空
                    stuId.setText("");
                    stuName.setText("");
                    stuSex.setText("");
                    stuAge.setText("");
                    //唤醒查询点击事件
                    selectBtn.callOnClick();
                }
            });
            //点击按钮查询数据,查询需要主键_id,这是SQLite的规定
            selectBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //游标
                    Cursor cursor = null;
                    //根据ID判断查一条信息还是查所有学生信息
                    if (stuId.getText().toString().trim().equals("")){
                        //发送请求,调用resolver的query方法并传入URI,管理该URI的ContentProvider捕获到该请求后会执行对应的query方法,返回结果给该请求
                        cursor = resolver.query(Uri.parse("content://uri.mycontentprovider/studentall"),new String[]{"stuId as _id","stuName","stuSex","stuAge"},null,null,null);
                    }else{
                        //发送请求,调用resolver的query方法并传入URI,管理该URI的ContentProvider捕获到该请求后会执行对应的query方法,返回结果给该请求
                        cursor = resolver.query(Uri.parse("content://uri.mycontentprovider/student/"+stuId.getText().toString()),new String[]{"stuId as _id","stuName","stuSex","stuAge"},"_id = ?",new String[]{stuId.getText().toString()},"");
                    }
                    //将查询结果通过适配器渲染到listview控件中
                    SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(MainActivity.this,R.layout.one,cursor,new String[]{"_id","stuName","stuSex","stuAge"},new int[]{R.id.oneid,R.id.onename,R.id.onesex,R.id.oneage});
                    listview.setAdapter(cursorAdapter);
                    //消息弹框
                    Toast.makeText(MainActivity.this,"远程查询成功",Toast.LENGTH_LONG).show();
                }
            });
            //点击某一列表项触发该事件,将列表信息填入输入框
            listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    TextView id = view.findViewById(R.id.oneid);
                    TextView name = view.findViewById(R.id.onename);
                    TextView age = view.findViewById(R.id.oneage);
                    TextView sex = view.findViewById(R.id.onesex);
                    //获取数据
                    stuId.setText(id.getText());
                    stuName.setText(name.getText());
                    stuSex.setText(age.getText());
                    stuAge.setText(sex.getText());
                }
            });
            //点击按钮删除数据
            deleteBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //发送请求,调用resolver的delete方法并传入URI,管理该URI的ContentProvider捕获到该请求后会执行对应的delete方法,返回结果给该请求
                    resolver.delete(Uri.parse("content://uri.mycontentprovider/deleteStudent"),"stuId=?",new String[]{stuId.getText().toString()});
                    //消息弹框
                    Toast.makeText(MainActivity.this,"远程删除成功",Toast.LENGTH_LONG).show();
                    //清空输入框数据
                    stuId.setText("");
                    stuName.setText("");
                    stuSex.setText("");
                    stuAge.setText("");
                    //唤醒查询点击事件
                    selectBtn.callOnClick();
                }
            });
            //修改数据
            updateBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ContentValues contentValues = new ContentValues();
                    contentValues.put("stuName",stuName.getText().toString());
                    contentValues.put("stuAge",stuAge.getText().toString());
                    contentValues.put("stuSex",stuSex.getText().toString());
                    //发送请求,调用resolver的update方法并传入URI,管理该URI的ContentProvider捕获到该请求后会执行对应的delete方法,返回结果给该请求
                    resolver.update(Uri.parse("content://uri.mycontentprovider/updateStudent"),contentValues,"stuId=?",new String[]{stuId.getText().toString()});
                    //消息弹框
                    Toast.makeText(MainActivity.this,"远程修改成功",Toast.LENGTH_LONG).show();
                    //将输入框数据置空
                    stuId.setText("");
                    stuName.setText("");
                    stuSex.setText("");
                    stuAge.setText("");
                    //唤醒查询点击事件
                    selectBtn.callOnClick();
                }
            });
        }
        //监听器类,监听ContentProvider的某个URI下数据的变化
        class MyObserver extends ContentObserver {
            public MyObserver(Handler handler) {
                super(handler);
                Toast.makeText(MainActivity.this,"开始",Toast.LENGTH_LONG).show();
            }
            //URI下的数据发生变化时会回调该方法
            public void onChange(boolean selfChange, Uri uri) {
                //消息弹框,通知所有注册了监听的Application该URI下的数据发生变化
                Toast.makeText(MainActivity.this,"来自ContentObserver的通知:ContentProvider的数据发生改变",Toast.LENGTH_LONG).show();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    (2)布局文件:
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="学号:"
                android:textSize="20sp"
                android:gravity="center"/>
            <EditText
                android:id="@+id/stuId"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:drawableBottom="@drawable/border_bottom"/>
        LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="姓名:"
                android:textSize="20sp"
                android:gravity="center"/>
            <EditText
                android:id="@+id/stuName"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:drawableBottom="@drawable/border_bottom"/>
        LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="性别:"
                android:textSize="20sp"
                android:gravity="center"/>
            <EditText
                android:id="@+id/stuSex"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:drawableBottom="@drawable/border_bottom"/>
        LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="年龄:"
                android:textSize="20sp"
                android:gravity="center"/>
            <EditText
                android:id="@+id/stuAge"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:drawableBottom="@drawable/border_bottom"/>
        LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:id="@+id/addbtn"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="添加"/>
            <Button
                android:id="@+id/deletebtn"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="删除"/>
            <Button
                android:id="@+id/updatebtn"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="修改"/>
            <Button
                android:id="@+id/selectbtn"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="查询"/>
        LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >
            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="学号"/>
            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="姓名"/>
            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="性别"/>
            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="年龄"/>
        LinearLayout>
        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        ListView>
    LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:id="@+id/one">
        <TextView
            android:id="@+id/oneid"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/onename"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/onesex"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/oneage"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
    LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4.运行效果:

    必须先运行app创建数据库,之后才能运行app2操作app的数据库。
    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    chap8-fluent python
    Javascript正则解析出代码的函数体
    华为OD机试 - 热点网站统计 - 逻辑分析(Java 2023 B卷 100分)
    大数据1.5 使用Vim编辑器
    什么测试自动化测试?
    华为机试 - 机器人走迷宫
    小程序毕设作品之微信体育馆预约小程序毕业设计成品(8)毕业设计论文模板
    YOLOv7优化:独家创新(Partial_C_Detect)检测头结构创新,实现涨点 | 检测头新颖创新系列
    算法训练day36|贪心算法 part05(重叠区间三连击:LeetCode435. 无重叠区间763.划分字母区间56. 合并区间)
    [单片机课程设计报告汇总] 单片机设计报告常用硬件元器件描述
  • 原文地址:https://blog.csdn.net/m0_53881899/article/details/128077531