AndroidContentProvider ContentResolver和ContentObserver的使用

http://www.it165.net/pro/html/201406/15820.html

1、ContentProvider、ContentResolver和ContentObserver

ContentProvider是Android的四大組件之一,可見它在Android中的作用非同小可。它主要的作用是:實(shí)現(xiàn)各個(gè)應(yīng)用程序之間的(跨應(yīng)用)數(shù)據(jù)共享,比如聯(lián)系人應(yīng)用中就使用了ContentProvider,你在自己的應(yīng)用中可以讀取和修改聯(lián)系人的數(shù)據(jù),不過需要獲得相應(yīng)的權(quán)限。其實(shí)它也只是一個(gè)中間人,真正的數(shù)據(jù)源是文件或者SQLite等。

一個(gè)應(yīng)用實(shí)現(xiàn)ContentProvider來提供內(nèi)容給別的應(yīng)用來操作, 通過ContentResolver來操作別的應(yīng)用數(shù)據(jù),當(dāng)然在自己的應(yīng)用中也可以。

ContentObserver——內(nèi)容觀察者,目的是觀察(捕捉)特定Uri引起的數(shù)據(jù)庫的變化,繼而做一些相應(yīng)的處理,它類似于數(shù)據(jù)庫技術(shù)中的觸發(fā)器(Trigger),當(dāng)ContentObserver所觀察的Uri發(fā)生變化時(shí),便會(huì)觸發(fā)它。觸發(fā)器分為表觸發(fā)器、行觸發(fā)器,相應(yīng)地ContentObserver也分為“表“ContentObserver、“行”ContentObserver,當(dāng)然這是與它所監(jiān)聽的Uri MIME Type有關(guān)的。

2、Contacts Demo

1)、基本功能實(shí)現(xiàn)

接下來通過一個(gè)簡(jiǎn)單的存儲(chǔ)聯(lián)系人信息的demo,來學(xué)習(xí)怎么創(chuàng)建自定義的ContentProvider,這里數(shù)據(jù)源選用SQLite,最常用的也是這個(gè)。

(1) 創(chuàng)建一個(gè)類NoteContentProvider,繼承ContentProvider,需要實(shí)現(xiàn)下面5個(gè)方法:

query

insert

update

delete

getType

view sourceprint?

01.publicclassContactsContentProviderextendsContentProvider{

02.

03.@Override

04.publicbooleanonCreate() {

05.// TODO Auto-generated method stub

06.returnfalse;

07.}

08.

09.@Override

10.publicintdelete(Uri arg0, String arg1, String[] arg2) {

11.// TODO Auto-generated method stub

12.return0;

13.}

14.

15.@Override

16.publicString getType(Uri arg0) {

17.// TODO Auto-generated method stub

18.returnnull;

19.}

20.

21.@Override

22.publicUri insert(Uri arg0, ContentValues arg1) {

23.// TODO Auto-generated method stub

24.returnnull;

25.}

26.

27.@Override

28.publicCursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,

29.String arg4) {

30.// TODO Auto-generated method stub

31.returnnull;

32.}

33.

34.@Override

35.publicintupdate(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {

36.// TODO Auto-generated method stub

37.return0;

38.}

39.

40.}

(2)先來設(shè)計(jì)一個(gè)數(shù)據(jù)庫,用來聯(lián)系人信息,主要包含_ID,name,telephone,create_date,content五個(gè)字段。group_name字段等后面升級(jí)部分再做使用。創(chuàng)建ProviderMetaData類,封裝URI和數(shù)據(jù)庫、表、字段相關(guān)信息,源碼如下:

view sourceprint?

01.publicclassProviderMetaData {

02.

03.publicstaticfinalString AUTHORITY ="com.johnny.contactsprovider";

04.publicstaticfinalUri AUTHORITY_URI = Uri.parse("content://"+ AUTHORITY);

05.

06.publicstaticfinalclassContactsDataimplementsBaseColumns{

07.publicstaticfinalString TABLE_NAME ="contacts";

08.publicstaticfinalUri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, TABLE_NAME);

09.

10.publicstaticfinalString CONTENT_TYPE ="vnd.android.cursor.dir/contact";

11.publicstaticfinalString CONTENT_ITEM_TYPE ="vnd.android.cursor.item/contact";

12.

13.publicstaticfinalString CONTACT_NAME ="name";

14.publicstaticfinalString CONTACT_TELEPHONE ="telephone";

15.publicstaticfinalString CONTACT_CREATE_DATE ="create_date";

16.publicstaticfinalString CONTACT_CONTENT ="content";

17.publicstaticfinalString CONTACT_GROUP ="group_name";

18.

19.publicstaticfinalString DEFAULT_ORDERBY ="create_date DESC";

20.

21.publicstaticfinalString SQL_CREATE_TABLE ="CREATE TABLE "+ TABLE_NAME +" ("

22.+ _ID +" INTEGER PRIMARY KEY,"

23.+ CONTACT_NAME +" VARCHAR(50),"

24.+ CONTACT_TELEPHONE +" VARCHAR(11),"

25.+ CONTACT_CONTENT +" TEXT,"

26.+ CONTACT_CREATE_DATE +" INTEGER"

27.+");";

28.}

29.}

AUTHORITY代表授權(quán),該字符串和在Android描述文件AndroidManifest.xml中注冊(cè)該ContentProvider時(shí)的android:authorities值一樣,ContactsData繼承BaseColumns,后者提供了標(biāo)準(zhǔn)的_id字段,表示行ID。

熟悉Content Provider(內(nèi)容提供者)的應(yīng)該知道,我們可以通過UriMatcher類注冊(cè)不同類型的Uri,我們可以通過這些不同的Uri來查詢不同的結(jié)果。根據(jù)Uri返回的結(jié)果,Uri Type可以分為:返回多條數(shù)據(jù)的Uri、返回單條數(shù)據(jù)的Uri。

Android遵循類似的約定來定義MIME類型,每個(gè)內(nèi)容類型的Android MIME類型有兩種形式:多條記錄(集合)和單條記錄。

多條記錄

vnd.android.cursor.dir/contact

單條記錄

vnd.android.cursor.item/contact

vnd表示這些類型和子類型具有非標(biāo)準(zhǔn)的、供應(yīng)商特定的形式。Android中類型已經(jīng)固定好了,不能更改,只能區(qū)別是集合還是單條具體記錄,子類型/之后的內(nèi)容可以按照格式隨便填寫。在使用Intent時(shí),會(huì)用到MIME這玩意,根據(jù)Mimetype打開符合條件的活動(dòng)。

(3) ContentProvider是根據(jù)URI來獲取數(shù)據(jù)的,那它怎么區(qū)分不同的URI呢,因?yàn)闊o論是獲取筆記列表還是獲取一條筆記都是調(diào)用query方法,現(xiàn)在來實(shí)現(xiàn)這個(gè)功能。需要用到類UriMatcher,該類可以幫助我們識(shí)別URI類型,下面看實(shí)現(xiàn)源碼:

view sourceprint?

01.staticfinalUriMatcher URI_MATCHER =newUriMatcher(UriMatcher.NO_MATCH);

02.staticfinalHashMap CONTACTS_PROJECTION_MAP =newHashMap();

03.privatestaticfinalintCONTACTS =1;

04.privatestaticfinalintCONTACTS_ID =2;

05.static{

06.finalUriMatcher matcher = URI_MATCHER;

07.matcher.addURI(ProviderMetaData.AUTHORITY,"contacts", CONTACTS);

08.matcher.addURI(ProviderMetaData.AUTHORITY,"contacts/#", CONTACTS_ID);

09.

10.HashMap map = CONTACTS_PROJECTION_MAP;

11.map.put(ContactsData._ID, ContactsData._ID);

12.map.put(ContactsData.CONTACT_NAME, ContactsData.CONTACT_NAME);

13.map.put(ContactsData.CONTACT_TELEPHONE, ContactsData.CONTACT_TELEPHONE);

14.map.put(ContactsData.CONTACT_CONTENT, ContactsData.CONTACT_CONTENT);

15.map.put(ContactsData.CONTACT_CREATE_DATE, ContactsData.CONTACT_CREATE_DATE);

16.}

這段代碼是NoteContentProvider類中的,UriMatcher的工作原理:首先需要在UriMatcher中注冊(cè)URI模式,每一個(gè)模式跟一個(gè)唯一的編號(hào)關(guān)聯(lián),注冊(cè)之后,在使用中就可以根據(jù)URI得到對(duì)應(yīng)的編號(hào),當(dāng)模式不匹配時(shí),UriMatcher將返回一個(gè)NO_MATCH常量,這樣就可以區(qū)分了。

(4) 還需為查詢?cè)O(shè)置一個(gè)投影映射,主要是將抽象字段映射到數(shù)據(jù)庫中真實(shí)的字段名稱,因?yàn)檫@些字段有時(shí)是不同的名稱,既抽象字段的值可以不跟數(shù)據(jù)庫中的字段名稱一樣。這里使用HashMap來完成,key是抽象字段名稱,value對(duì)應(yīng)數(shù)據(jù)庫中的字段名稱,不過這里我把兩者的值設(shè)置是一樣的,在NoteContentProvider.java中添加如上面所示的代碼。

(5) 在NoteContentProvider.java中創(chuàng)建一個(gè)內(nèi)部類DatabaseHelper,繼承自SQLiteOpenHelper,完成數(shù)據(jù)庫表的創(chuàng)建、更新,這樣可以通過它獲得數(shù)據(jù)庫對(duì)象,相關(guān)代碼如下。

view sourceprint?

01.privateclassDatabaseHelperextendsSQLiteOpenHelper{

02.

03.staticfinalString DATABASE_NAME ="test.db";

04.staticfinalintDATABASE_VERSION =1;

05.

06.publicDatabaseHelper(Context context) {

07.super(context, DATABASE_NAME,null, DATABASE_VERSION);

08.// TODO Auto-generated constructor stub

09.}

10.

11.@Override

12.publicvoidonCreate(SQLiteDatabase db) {

13.// TODO Auto-generated method stub

14.db.execSQL(ContactsData.SQL_CREATE_TABLE);

15.}

16.

17.@Override

18.publicvoidonUpgrade(SQLiteDatabase db,intoldVersion,intnewVersion) {

19.// TODO Auto-generated method stub

20.onCreate(db);

21.}

22.

23.}

(6) 現(xiàn)在來分別實(shí)現(xiàn)第一步中未實(shí)現(xiàn)的5個(gè)方法,先來實(shí)現(xiàn)query方法,這里借助SQLiteQueryBuilder來為查詢?cè)O(shè)置投影映射以及設(shè)置相關(guān)查詢條件,看源碼實(shí)現(xiàn):

view sourceprint?

01.@Override

02.publicCursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,

03.String sortOrder) {

04.// TODO Auto-generated method stub

05.SQLiteQueryBuilder queryBuilder =newSQLiteQueryBuilder();

06.switch(URI_MATCHER.match(uri)){

07.caseCONTACTS_ID:

08.queryBuilder.setTables(ContactsData.TABLE_NAME);

09.queryBuilder.setProjectionMap(CONTACTS_PROJECTION_MAP);

10.queryBuilder.appendWhere(ContactsData.TABLE_NAME +"._id="+Long.toString(ContentUris.parseId(uri)));

11.break;

12.caseCONTACTS:

13.queryBuilder.setTables(ContactsData.TABLE_NAME);

14.queryBuilder.setProjectionMap(CONTACTS_PROJECTION_MAP);

15.break;

16.}

17.

18.String orderBy;

19.if(TextUtils.isEmpty(sortOrder))

20.{

21.orderBy = ContactsData.DEFAULT_ORDERBY;

22.}else{

23.orderBy = sortOrder;

24.}

25.SQLiteDatabase db = mDbHelper.getReadableDatabase();

26.Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs,null,null, orderBy);

27.

28.returncursor;

29.}

返回的是一個(gè)Cursor對(duì)象,它是一個(gè)行集合,包含0和多個(gè)記錄,類似于JDBC中的ResultSet,可以前后移動(dòng)游標(biāo),得到每行每列中的數(shù)據(jù)。注意的是,使用它需要調(diào)用moveToFirst(),因?yàn)橛螛?biāo)默認(rèn)是在第一行之前。

(7)實(shí)現(xiàn)insert方法,實(shí)現(xiàn)把記錄插入到基礎(chǔ)數(shù)據(jù)庫中,然后返回新創(chuàng)建的記錄的URI。

view sourceprint?

01.@Override

02.publicUri insert(Uri uri, ContentValues values) {

03.// TODO Auto-generated method stub

04.SQLiteDatabase db = mDbHelper.getWritableDatabase();

05.longid = db.insertOrThrow(ContactsData.TABLE_NAME,null, values);

06.

07.// 更新數(shù)據(jù)時(shí),通知其他ContentObserver

08.getContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI,null);

09.

10.if(id >0){

11.returnContentUris.withAppendedId(uri, id);

12.}

13.returnnull;

14.}

(8) 實(shí)現(xiàn)update方法,根據(jù)傳入的列值和where字句來更新記錄,返回更新的記錄數(shù),看源碼:

view sourceprint?

01.@Override

02.publicintupdate(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

03.// TODO Auto-generated method stub

04.SQLiteDatabase db = mDbHelper.getWritableDatabase();

05.intmodified =0;

06.switch(URI_MATCHER.match(uri)){

07.caseCONTACTS_ID:

08.selection = DatabaseUtils.concatenateWhere(selection,ContactsData.TABLE_NAME +"._id=?");

09.selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,

10.newString[]{Long.toString(ContentUris.parseId(uri))});

11.Log.d("Test","selectionArgs 0"+selectionArgs);

12.modified = db.update(ContactsData.TABLE_NAME, values, selection, selectionArgs);

13.break;

14.caseCONTACTS:

15.modified = db.update(ContactsData.TABLE_NAME, values, selection, selectionArgs);

16.Log.d("Test","selectionArgs 1"+selectionArgs);

17.break;

18.}

19.

20.// 更新數(shù)據(jù)時(shí),通知其他ContentObserver

21.getContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI,null);

22.

23.returnmodified;

24.}

notifyChange函數(shù)是在更新數(shù)據(jù)時(shí),通知其他監(jiān)聽對(duì)象。

(9)實(shí)現(xiàn)delete方法,該方法返回刪除的記錄數(shù)。

view sourceprint?

01.@Override

02.publicintdelete(Uri uri, String selection, String[] selectionArgs) {

03.// TODO Auto-generated method stub

04.SQLiteDatabase db = mDbHelper.getWritableDatabase();

05.intdeleted =0;

06.switch(URI_MATCHER.match(uri)){

07.caseCONTACTS_ID:

08.selection = DatabaseUtils.concatenateWhere(selection,ContactsData.TABLE_NAME +"._id=?");

09.selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,

10.newString[]{Long.toString(ContentUris.parseId(uri))});

11.Log.d("Test","selectionArgs 0"+selectionArgs);

12.deleted = db.delete(ContactsData.TABLE_NAME, selection, selectionArgs);

13.break;

14.caseCONTACTS:

15.deleted = db.delete(ContactsData.TABLE_NAME, selection, selectionArgs);

16.Log.d("Test","selectionArgs 1"+selectionArgs);

17.break;

18.}

19.

20.// 更新數(shù)據(jù)時(shí),通知其他ContentObserver

21.getContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI,null);

22.

23.returndeleted;

24.}

(10) 實(shí)現(xiàn)getType方法,根據(jù)URI返回MIME類型,這里主要用來區(qū)分URI是獲取集合還是單條記錄,這個(gè)方法在這里暫時(shí)沒啥用處,在使用Intent時(shí)有用。

view sourceprint?

01.@Override

02.publicString getType(Uri uri) {

03.// TODO Auto-generated method stub

04.switch(URI_MATCHER.match(uri)){

05.caseCONTACTS:

06.returnContactsData.CONTENT_TYPE;

07.caseCONTACTS_ID:

08.returnContactsData.CONTENT_ITEM_TYPE;

09.//??????? default:

10.//??????????? throw new IllegalArgumentException("Unknow URI: " + uri);

11.}

12.returnnull;

13.}

(11) 在AndroidManifest.xml中注冊(cè)該ContentProvider,這樣系統(tǒng)才找得到,當(dāng)然你也可以設(shè)置相關(guān)的權(quán)限,這里就不設(shè)置了

view sourceprint?

1.

2.android:name="com.johnny.testcontentprovider.ContactsContentProvider"

3.android:authorities="com.johnny.contactsprovider">

4.

5.

(12)到現(xiàn)在為止,自定義ContentProvider的全部代碼已經(jīng)完成,下面創(chuàng)建一個(gè)簡(jiǎn)單的應(yīng)用來測(cè)試一下。

主要測(cè)試insert、update、delete、query這四個(gè)函數(shù)。

view sourceprint?

01.privatevoidinsertContact1(){

02.ContentValues values =newContentValues();

03.values.put(ContactsData.CONTACT_NAME,"James");

04.values.put(ContactsData.CONTACT_TELEPHONE,"18888888888");

05.values.put(ContactsData.CONTACT_CONTENT,"NBA Star");

06.values.put(ContactsData.CONTACT_CREATE_DATE, System.currentTimeMillis());

07.Uri uri = getContentResolver().insert(ContactsData.CONTENT_URI, values);

08.Log.d("Test","uri = "+uri);

09.}

10.

11.privatevoiddeleteContact1(){

12.intcount = getContentResolver().delete(ContactsData.CONTENT_URI, ContactsData.CONTACT_NAME+"='James'",null);

13.Log.d("Test","count = "+count);

14.}

15.

16.privatevoidupdateContact1(){

17.ContentValues values =newContentValues();

18.values.put(ContactsData.CONTACT_TELEPHONE,"16666666666");

19.intcount = getContentResolver().update(ContactsData.CONTENT_URI,values, ContactsData.CONTACT_NAME+"='James'",null);

20.Log.d("Test","count = "+count);

21.}

22.

23.privatevoidqueryContact1(){

24.Cursor cursor =this.getContentResolver().query(ContactsData.CONTENT_URI,null, ContactsData.CONTACT_NAME+"='James'",null,null);

25.Log.e("test ","count="+ cursor.getCount());

26.cursor.moveToFirst();

27.while(!cursor.isAfterLast()) {

28.String name = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_NAME));

29.String telephone = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_TELEPHONE));

30.longcreateDate = cursor.getLong(cursor.getColumnIndex(ContactsData.CONTACT_CREATE_DATE));

31.Log.e("Test","name: "+ name);

32.Log.e("Test","telephone: "+ telephone);

33.Log.e("Test","date: "+ createDate);

34.

35.cursor.moveToNext();

36.}

37.cursor.close();

38.}

(13)創(chuàng)建數(shù)據(jù)庫監(jiān)聽器ContentObserver

在MainActivity中加入以下代碼:

view sourceprint?

01.privateContentObserver mContentObserver =newContentObserver(newHandler()) {

02.

03.@Override

04.publicvoidonChange(booleanselfChange) {

05.// TODO Auto-generated method stub

06.Log.d("Test","mContentObserver onChange");

07.super.onChange(selfChange);

08.}

09.

10.};

11.@Override

12.protectedvoidonCreate(Bundle savedInstanceState) {

13.super.onCreate(savedInstanceState);

14.setContentView(R.layout.activity_main);

15.

16.if(savedInstanceState ==null) {

17.getSupportFragmentManager().beginTransaction()

18..add(R.id.container,newPlaceholderFragment()).commit();

19.}

20.

21.getContentResolver().registerContentObserver(ContactsData.CONTENT_URI,true, mContentObserver);

22.

23.}

每次通過insert、delete、update改變數(shù)據(jù)庫內(nèi)容時(shí),都會(huì)調(diào)用ContentObserver的onChange方法,因此,可以在這個(gè)方法內(nèi)做出針對(duì)數(shù)據(jù)庫變化的反應(yīng),比如更新UI等。

2)、數(shù)據(jù)庫的升級(jí)

當(dāng)應(yīng)用發(fā)布一段時(shí)間之后,我們需要改變數(shù)據(jù)庫的結(jié)構(gòu),那么就需要對(duì)數(shù)據(jù)庫的升級(jí)了:

將DatabaseHelper類中的DATABASE_VERSION設(shè)置為2,并且在onUpgrade函數(shù)中實(shí)現(xiàn)升級(jí)的代碼:

view sourceprint?

01.privateclassDatabaseHelperextendsSQLiteOpenHelper{

02.

03.staticfinalString DATABASE_NAME ="test.db";

04.staticfinalintDATABASE_VERSION =2;

05.

06.publicDatabaseHelper(Context context) {

07.super(context, DATABASE_NAME,null, DATABASE_VERSION);

08.// TODO Auto-generated constructor stub

09.}

10.

11.@Override

12.publicvoidonCreate(SQLiteDatabase db) {

13.// TODO Auto-generated method stub

14.db.execSQL(ContactsData.SQL_CREATE_TABLE);

15.}

16.

17.@Override

18.publicvoidonUpgrade(SQLiteDatabase db,intoldVersion,intnewVersion) {

19.// TODO Auto-generated method stub

20.Log.d("Test","onUpgrade oldVersion = "+oldVersion+", newVersion = "+newVersion);

21.//onCreate(db);

22.for(inti = oldVersion+1;i <= newVersion;i++){

23.switch(i){

24.case2:

25.db.execSQL("ALTER TABLE "+ ContactsData.TABLE_NAME +" ADD COLUMN "+ ContactsData.CONTACT_GROUP +" TEXT");

26.break;

27.}

28.}

29.}

30.

31.}

下面是升級(jí)前后數(shù)據(jù)庫的結(jié)果:

用下面代碼為DATABASE_VERSION = 2的數(shù)據(jù)庫中的James設(shè)在組別和加入Howard聯(lián)系人:

view sourceprint?

01.privatevoidmodifyContact1(){

02.ContentValues values =newContentValues();

03.values.put(ContactsData.CONTACT_GROUP,"Miami");

04.intcount = getContentResolver().update(ContactsData.CONTENT_URI,values, ContactsData.CONTACT_NAME+"='James'",null);

05.Log.d("Test","count = "+count);

06.}

07.

08.privatevoidinsertContact2(){

09.ContentValues values =newContentValues();

10.values.put(ContactsData.CONTACT_NAME,"Howard");

11.values.put(ContactsData.CONTACT_TELEPHONE,"13333333333");

12.values.put(ContactsData.CONTACT_CONTENT,"NBA Star");

13.values.put(ContactsData.CONTACT_GROUP,"Rockets");

14.values.put(ContactsData.CONTACT_CREATE_DATE, System.currentTimeMillis());

15.Uri uri = getContentResolver().insert(ContactsData.CONTENT_URI, values);

16.Log.d("Test","uri = "+uri);

17.}

結(jié)果如下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容