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