欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ContentResolver,ContentProvider,ContentObserver使用记录

程序员文章站 2024-02-09 16:50:04
...

一、ContentProvider概述

Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。为了增强程序之间的数据共享能力,Android系统提供了像SharedPreferences这类简单的跨越程序边界的访问方法,但这些方法都存在一定的局限性,提供数据的能力有限,安卓系统提供了另一种跨进程提供数据的方式也就ContentProvider,ContentProvider翻译过来叫做:数据提供者,是应用程序之间共享数据的一种接口机制,其他应用程序则可以在不知道数据来源的情况下,对共享数据进行增删改查等操作。在Android系统中,许多系统内置的数据也是通过ContentProvider提供给用户使用,例如通讯录、音视频图像文件等。

二、ContentProvider调用

调用者不能直接调用ContentProvider的接口函数,需要通过ContentResolver对象,通过URI间接调用ContentProvider,Android系统根据URI确定处理这个查询的ContentProvider。

三、通用资源标识符URI

URI可以理解为一个个网站的访问地址,比如百度有百度的地址,阿里有阿里的地址,同样在安卓系统中每个ContentProvider也都有自己的访问地址,ContentProvider使用的URI语法结构如下:

content:///<data_path>/
content:// 是通用前缀,表示该UIR用于ContentProvider定位资源。
< authority > 是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此一般< authority >都由类的小写全称组成,以保证唯一性。
< data_path > 是数据路径,用来确定请求的是哪个数据集。如果ContentProvider只提供一个数据集,数据路径则可以省略;如果ContentProvider提供多个数据集,数据路径必须指明具体数据集。数据集的数据路径可以写成多段格式,例如people/delete和people/insert。
< id > 是数据编号,用来唯一确定数据集中的一条记录,匹配数据集中_ID字段的值。如果请求的数据不只一条,< id >可以省略。
四、创建ContentProvider

创建一个类继承ContentProvider,重载6个函数,分别为onCreate(),getType(),insert()、delete()、update()、query()。

onCreate()
一般用来初始化底层数据集和建立数据连接等工作

getType()
用来返回指定URI的MIME数据类型,若URI是单条数据,则返回的MIME数据类型以vnd.android.cursor.item开头;若URI是多条数据,则返回的MIME数据类型以vnd.android.cursor.dir/开头。

insert()、delete()、update()、query()
用于对数据集的增删改查操作。

五、UriMatcher类

UriMatcher类其实就是一个工具类,用于匹配用户传递进来的Uri。

示例:

 1     private static final int PRESON_INSERT_CODE = 0;
 2     private static final int PERSON_DELETE_CODE = 1;
 3     private static final int PERSON_UPDATE_CODE = 2;
 4     private static final int PERSON_QUERY_ALL_CODE = 3;
 5     private static final int PERSON_QUERY_ITEM_CODE = 4;
 6     //
 7     private static UriMatcher uriMatcher;
 8     private PersonSQLiteOpenHelper mOpenHelper;
 9 
10     static {
11          uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
12 
13         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_INSERT,
14                 PRESON_INSERT_CODE);
15         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_DELETE,
16                 PERSON_DELETE_CODE);
17         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_UPDATE,
18                 PERSON_UPDATE_CODE);
19         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ALL,
20                 PERSON_QUERY_ALL_CODE);
21         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ITEM,
22                 PERSON_QUERY_ITEM_CODE);
23     }

UriMatcher的构造函数中,UriMatcher.NO_MATCH是URI无匹配时的返回代码,值为-1。 addURI() 方法用来添加新的匹配项,语法为:

public void addURI(String authority, String path, int code)
其中authority表示匹配的授权者名称,path表示数据路径,code表示匹配成功时的返回代码。

使用示例:

 1     @Override
 2     public String getType(Uri uri) {
 3         switch (uriMatcher.match(uri)) {
 4         case PERSON_QUERY_ALL_CODE: // 返回多条的MIME-type
 5             return "vnd.android.cursor.dir/person";
 6         case PERSON_QUERY_ITEM_CODE: // 返回单条的MIME-TYPE
 7             return "vnd.android.cursor.item/person";
 8         default:
 9             break;
10         }
11         return null;
12     }

六、ContentObserver简要介绍

ContentObserver——内容观察者,观察)特定Uri引起的数据库的变化,继而做一些相应的处理,当ContentObserver所观察的Uri发生变化时,便会触发它回调onChange方法。

ContentObserver的编写:创建一个类继承自ContentObserver,重写onChange,监听的的url数据发生变化时就会回调此方法。

示例:

 1 public class PersonContentObserver extends ContentObserver {
 2 
 3     //
 4     private static final String TAG = "TestCase";
 5     private Context mContext;
 6     
 7     public PersonContentObserver(Handler handler,Context mContext) {
 8         super(handler);
 9         this.mContext = mContext;
10     }
11     
12     @Override
13     public void onChange(boolean selfChange) {
14         //
1516     }
17 
18 }

ContentObserver的注册:ContentObserver的注册是由ContentResolver来完成的。

示例:

 1 public class MainActivity extends Activity {
 2 
 3     private PersonContentObserver mContentObserver;
 4     
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         
10         mContentObserver = new PersonContentObserver(new Handler(),this);
11         getContentResolver().registerContentObserver(Person.CONTENT_URI_DELETE,
12                 true, mContentObserver);
13     }
14     
15     @Override
16     protected void onDestroy() {
17         //
18        super.onDestroy();
19 
20       getContentResolver().unregisterContentObserver(mContentObserver);
21     }
22 }

void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendents, @NonNull ContentObserver observer)参数说明

uri:监测的uri地址

notifyForDescendents:为true 表示可以同时匹配其派生的Uri,false只精确匹配当前Uri.

observer:就是我们自己编写的ContentObserve了。

七、Demo源码示例

1,编写ContentProvider工程,此工程演示ContentProvider的创建以及ContentObserver的使用

工程目录:

先来看看Person类:

 1 public class Person {
 2 
 3     public static final String AUTHORITY = "com.wanglei.personcontentprovider";
 4     //
 5     public static final String PATH_INSERT = "person/insert";
 6     public static final String PATH_DELETE = "person/delete";
 7     public static final String PATH_UPDATE = "person/update";
 8     public static final String PATH_QUERY_ALL = "person/queryAll";
 9     public static final String PATH_QUERY_ITEM = "person/query/#";
10     //
11     public static final Uri CONTENT_URI_INSERT = Uri.parse("content://" + AUTHORITY + "/" + PATH_INSERT);
12     public static final Uri CONTENT_URI_DELETE = Uri.parse("content://" + AUTHORITY + "/" + PATH_DELETE);
13     public static final Uri CONTENT_URI_UPDATE = Uri.parse("content://" + AUTHORITY + "/" + PATH_UPDATE);
14     public static final Uri CONTENT_URI_QUERY_ALL = Uri.parse("content://" + AUTHORITY + "/" + PATH_QUERY_ALL);
15     public static final Uri CONTENT_URI_QUERY_ITEM = Uri.parse("content://" + AUTHORITY + "/" + PATH_QUERY_ITEM);
16     //
17     public static final String KEY_ID = "_id";
18     public static final String KEY_NAME = "name";
19     public static final String KEY_AGE = "age";
20 }

此类只是定义了一些常量,由于只是演示,为了方便没有写成正规的javaBean.很简单,没有多余需要解释的。

接下来看下PersonSQLiteOpenHelper类,此类就是数据库的创建了,很简单,如下:

 1 /**
 2  * @author andong 数据库帮助类, 用于创建和管理数据库的.
 3  */
 4 public class PersonSQLiteOpenHelper extends SQLiteOpenHelper {
 5 
 6     //数据库名称
 7     private static final String DB_NAME = "person.db";
 8     //表名称
 9     public static final String TABLE_NAME = "person";
10 
11     /**
12      * 数据库的构造函数
13      * 
14      * @param context
15      * 
16      *            name 数据库名称 factory 游标工程 version 数据库的版本号 不可以小于1
17      */
18     public PersonSQLiteOpenHelper(Context context) {
19         super(context, DB_NAME, null, 1);
20     }
21 
22     /**
23      * 数据库第一次创建时回调此方法. 初始化一些表
24      */
25     @Override
26     public void onCreate(SQLiteDatabase db) {
27 
28         // 操作数据库
29         String sql = "create table " + TABLE_NAME + "(" + Person.KEY_ID
30                 + " integer primary key autoincrement, " + Person.KEY_NAME
31                 + " varchar(100), "+Person.KEY_AGE+" integer);";
32         db.execSQL(sql); // 创建person表
33     }
34 
35     /**
36      * 数据库的版本号更新时回调此方法, 更新数据库的内容(删除表, 添加表, 修改表)
37      */

38     @Override
39     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
40 
41         db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
42         onCreate(db);
43     }
44 }

接下来继续看下PersonContentProvider类:

  1 public class PersonContentProvider extends ContentProvider {
  2 
  3     private static final int PRESON_INSERT_CODE = 0;
  4     private static final int PERSON_DELETE_CODE = 1;
  5     private static final int PERSON_UPDATE_CODE = 2;
  6     private static final int PERSON_QUERY_ALL_CODE = 3;
  7     private static final int PERSON_QUERY_ITEM_CODE = 4;
  8     //
  9     private static UriMatcher uriMatcher;
 10     private PersonSQLiteOpenHelper mOpenHelper;
 11 
 12     static {
 13         uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 14 
 15         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_INSERT,
 16                 PRESON_INSERT_CODE);
 17         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_DELETE,
 18                 PERSON_DELETE_CODE);
 19         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_UPDATE,
 20                 PERSON_UPDATE_CODE);
 21         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ALL,
 22                 PERSON_QUERY_ALL_CODE);
 23         uriMatcher.addURI(Person.AUTHORITY, Person.PATH_QUERY_ITEM,
 24                 PERSON_QUERY_ITEM_CODE);
 25     }
 26 
 27     @Override
 28     public boolean onCreate() {
 29         mOpenHelper = new PersonSQLiteOpenHelper(getContext());
 30         return true;
 31     }
 32 
 33     @Override
 34     public Cursor query(Uri uri, String[] projection, String selection,
 35             String[] selectionArgs, String sortOrder) {
 36         SQLiteDatabase db = mOpenHelper.getReadableDatabase();
 37         switch (uriMatcher.match(uri)) {
 38         case PERSON_QUERY_ALL_CODE: // 查询所有人的uri
 39             if (db.isOpen()) {
 40                 Cursor cursor = db.query(PersonSQLiteOpenHelper.TABLE_NAME,
 41                         projection, selection, selectionArgs, null, null,
 42                         sortOrder);
 43                 cursor.setNotificationUri(getContext().getContentResolver(), uri);
 44                 return cursor;
 45                 // db.close(); 返回cursor结果集时, 不可以关闭数据库
 46             }
 47             break;
 48         case PERSON_QUERY_ITEM_CODE: // 查询的是单条数据, uri末尾出有一个id
 49             if (db.isOpen()) {
 50 
 51                 long id = ContentUris.parseId(uri);
 52 
 53                 Cursor cursor = db.query(PersonSQLiteOpenHelper.TABLE_NAME,
 54                         projection, Person.KEY_ID + " = ?", new String[] { id
 55                                 + "" }, null, null, sortOrder);
 56                 cursor.setNotificationUri(getContext().getContentResolver(), uri);
 57                 return cursor;
 58             }
 59             break;
 60         default:
 61             throw new IllegalArgumentException("uri不匹配: " + uri);
 62         }
 63         return null;
 64     }
 65 
 66     @Override
 67     public String getType(Uri uri) {
 68         switch (uriMatcher.match(uri)) {
 69         case PERSON_QUERY_ALL_CODE: // 返回多条的MIME-type
 70             return "vnd.android.cursor.dir/person";
 71         case PERSON_QUERY_ITEM_CODE: // 返回单条的MIME-TYPE
 72             return "vnd.android.cursor.item/person";
 73         default:
 74             break;
 75         }
 76         return null;
 77     }
 78 
 79     @Override
 80     public Uri insert(Uri uri, ContentValues values) {
 81 
 82         switch (uriMatcher.match(uri)) {
 83         case PRESON_INSERT_CODE: // 添加人到person表中
 84             SQLiteDatabase db = mOpenHelper.getWritableDatabase();
 85 
 86             if (db.isOpen()) {
 87 
 88                 long id = db.insert(PersonSQLiteOpenHelper.TABLE_NAME, null,
 89                         values);
 90 
 91                 db.close();
 92                 Uri newUri = ContentUris.withAppendedId(uri, id);
 93                 //通知内容观察者数据发生变化
 94                 getContext().getContentResolver().notifyChange(newUri, null);
 95                 return newUri;
 96             }
 97             break;
 98         default:
 99             throw new IllegalArgumentException("uri不匹配: " + uri);
100         }
101         return null;
102     }
103 
104     @Override
105     public int delete(Uri uri, String selection, String[] selectionArgs) {
106         switch (uriMatcher.match(uri)) {
107         case PERSON_DELETE_CODE: // 在person表中删除数据的操作
108             SQLiteDatabase db = mOpenHelper.getWritableDatabase();
109             if (db.isOpen()) {
110                 int count = db.delete(PersonSQLiteOpenHelper.TABLE_NAME,
111                         selection, selectionArgs);
112                 db.close();
113                 //通知内容观察者数据发生变化
114                 getContext().getContentResolver().notifyChange(uri, null);
115                 return count;
116             }
117             break;
118         default:
119             throw new IllegalArgumentException("uri不匹配: " + uri);
120         }
121         return 0;
122     }
123 
124     @Override
125     public int update(Uri uri, ContentValues values, String selection,
126             String[] selectionArgs) {
127         switch (uriMatcher.match(uri)) {
128         case PERSON_UPDATE_CODE: // 更新person表的操作
129             SQLiteDatabase db = mOpenHelper.getWritableDatabase();
130             if (db.isOpen()) {
131                 int count = db.update(PersonSQLiteOpenHelper.TABLE_NAME,
132                         values, selection, selectionArgs);
133                 db.close();
134                 //通知内容观察者数据发生变化
135                 getContext().getContentResolver().notifyChange(uri, null);
136                 return count;
137             }
138             break;
139         default:
140             throw new IllegalArgumentException("uri不匹配: " + uri);
141         }
142         return 0;
143     }
144 
145 }

可以看到此ContentProvider内部操作对象就是person.db中的person表,并且对数据库操作完调用 getContext().getContentResolver().notifyChange(uri, null)通知对应内容观察者数据发生了变化。

PersonContentObserver类:

 1 public class PersonContentObserver extends ContentObserver {
 2 
 3     //
 4     private static final String TAG = "TestCase";
 5     private Context mContext;
 6     
 7     public PersonContentObserver(Handler handler,Context mContext) {
 8         super(handler);
 9         this.mContext = mContext;
10     }
11     
12     @Override
13     public void onChange(boolean selfChange) {
14         //
15         Log.i(TAG, "PersonContentObserver");
16         ContentResolver resolver = mContext.getContentResolver();
17 
18         Cursor cursor = resolver
19                 .query(Person.CONTENT_URI_QUERY_ALL, new String[] {
20                         Person.KEY_ID, Person.KEY_NAME, Person.KEY_AGE }, null,
21                         null, "_id desc");
22 
23         if (cursor != null && cursor.getCount() > 0) {
24 
25             int id;
26             String name;
27             int age;
28             while (cursor.moveToNext()) {
29                 id = cursor.getInt(cursor.getColumnIndex(Person.KEY_ID));
30                 name = cursor.getString(cursor.getColumnIndex(Person.KEY_NAME));
31                 age = cursor.getInt(cursor.getColumnIndex(Person.KEY_AGE));
32                 Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
33             }
34             cursor.close();
35         }
36     }
37 }

在我们接收到数据发生变化的时候进行的操作是重新查询person表中所有数据。

最后在MainActivity中注册PersonContentObserver:

 1 public class MainActivity extends Activity {
 2 
 3     private PersonContentObserver mContentObserver;
 4     
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         
10         mContentObserver = new PersonContentObserver(new Handler(),this);
11         getContentResolver().registerContentObserver(Person.CONTENT_URI_DELETE,
12                 true, mContentObserver);
13     }
14     
15     @Override
16     protected void onDestroy() {
17         //
18         super.onDestroy();
19 
20         getContentResolver().unregisterContentObserver(mContentObserver);
21     }
22 } 

别忘了在清单文件中注册内容提供者:

1 <provider
2    android:name="com.wanglei.provider.PersonContentProvider"
3    android:authorities="com.wanglei.personcontentprovider"
4    android:exported="true" >
5 </provider>

接下来我们就要编写新项目测试我们的PersonContentProvider能不能正常使用以及PersonContentObserver能不能检测到数据发生发生变化了。

编写UseContentProvider项目,结构如下:

其中Person类和上面的Person类是一样的。

test.java类就是测试类了,测试增删改查:

 1 public class test extends AndroidTestCase {
 2 
 3     private static final String TAG = "TestCase";
 4 
 5     public void testInsert() {
 6         // 内容提供者访问对象
 7         ContentResolver resolver = getContext().getContentResolver();
 8 
 9         for (int i = 0; i < 10; i++) {
10             //
11             ContentValues values = new ContentValues();
12             values.put(Person.KEY_NAME, "wanglei"+i);
13             values.put(Person.KEY_AGE, i);
14             Uri uri = resolver.insert(Person.CONTENT_URI_INSERT, values);
15             Log.i(TAG, "uri: " + uri);
16             long id = ContentUris.parseId(uri);
17             Log.i(TAG, "添加到: " + id);
18         }
19     }
21     public void testDelete() {
22 
23         // 内容提供者访问对象
24         ContentResolver resolver = getContext().getContentResolver();
25         String where = Person.KEY_ID + " = ?";
26         String[] selectionArgs = { "3" };
27         int count = resolver.delete(Person.CONTENT_URI_DELETE, where,
28                 selectionArgs);
29         Log.i(TAG, "删除行: " + count);
30     }
2     public void testUpdate() {
33 
34         // 内容提供者访问对象
35         ContentResolver resolver = getContext().getContentResolver();
36 
37         ContentValues values = new ContentValues();
38         values.put(Person.KEY_NAME, "lisi");
39 
40         int count = resolver.update(Person.CONTENT_URI_UPDATE, values,
41                 Person.KEY_ID + " = ?", new String[] { "1" });
42         Log.i(TAG, "更新行: " + count);
43     }
44 
45     public void testQueryAll() {
46 
47         // 内容提供者访问对象
48         ContentResolver resolver = getContext().getContentResolver();
49 
50         Cursor cursor = resolver
51                 .query(Person.CONTENT_URI_QUERY_ALL, new String[] {
52                         Person.KEY_ID, Person.KEY_NAME, Person.KEY_AGE }, null,
53                         null, "_id desc");
54 
55         if (cursor != null && cursor.getCount() > 0) {
56 
57             int id;
58             String name;
59             int age;
60             while (cursor.moveToNext()) {
61                 id = cursor.getInt(cursor.getColumnIndex(Person.KEY_ID));
62                 name = cursor.getString(cursor.getColumnIndex(Person.KEY_NAME));
63                 age = cursor.getInt(cursor.getColumnIndex(Person.KEY_AGE));
64                 Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
65             }
66             cursor.close();
67         }
68     }
69 
70     public void testQuerySingleItem() {
71 
72         // 在uri的末尾添加一个id
73         Uri uri = ContentUris.withAppendedId(Person.CONTENT_URI_QUERY_ITEM, 1);
74 
75         // 内容提供者访问对象
76         ContentResolver resolver = getContext().getContentResolver();
77 
78         Cursor cursor = resolver.query(uri, new String[] { Person.KEY_ID,
79                 Person.KEY_NAME, Person.KEY_AGE }, null, null, null);
80 
81         if (cursor != null && cursor.moveToFirst()) {
82             int id = cursor.getInt(0);
83             String name = cursor.getString(1);
84             int age = cursor.getInt(2);
85             cursor.close();
86             Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age);
87         }
88     }
89 }

好了,本片只是个人记录一些经常忘记的知识点方便以后忘记了可以翻翻,没有特别仔细分析。

相关标签: ContentProvide

上一篇: if语句

下一篇: I