ContentResolver,ContentProvider,ContentObserver使用记录

版权声明:本文出自汪磊的博客,转载请务必注明出处。

本篇博客只是记录一下ContentProvider的使用(这部分工作中用的比较少总是忘记),没有太深入研究已经熟练掌握使用方式,想深入了解内部机制的同学可以绕过了。

一、ContentProvider概述

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

二、ContentProvider调用

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

三、通用资源标识符URI

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

 content://<authority>/<data_path>/<id>
  • 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     }
20 
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     }
31 
32     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 }

复制代码

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

猜你喜欢

转载自blog.csdn.net/u010871962/article/details/107675940