zoukankan      html  css  js  c++  java
  • 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 }

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

  • 相关阅读:
    C#中实现窗体间传值开发者在线 Builder.com.cn 更新时间:20080719作者: 来源:
    Visual C# 2005中如何产生与比较哈希值开发者在线 Builder.com.cn 更新时间:20080720作者: 来源:
    C#多线程编程实例实战开发者在线 Builder.com.cn 更新时间:20080720作者: 来源:
    如何构造一个C#语言的爬虫程序开发者在线 Builder.com.cn 更新时间:20080720作者: 来源:
    用C#读取Windows注册表中的多重字串和二进制字串开发者在线 Builder.com.cn 更新时间:20080720作者: 来源:
    Visual C#多线程参数传递浅析开发者在线 Builder.com.cn 更新时间:20080719作者: 来源:
    C#操作消息队列的代码开发者在线 Builder.com.cn 更新时间:20080720作者: 来源:
    深入解析C#编程中的事件开发者在线 Builder.com.cn 更新时间:20080719作者: 来源:
    理解C# 3.0新特性之Extension方法浅议开发者在线 Builder.com.cn 更新时间:20080719作者: 来源:
    什么东西打印出来好查,什么东西放在电脑好查
  • 原文地址:https://www.cnblogs.com/leipDao/p/8136093.html
Copyright © 2011-2022 走看看