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 }

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

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM