Android ContentProvider、ContentResolver和ContentObserver的使用
1、ContentProvider、ContentResolver和ContentObserver
ContentProvider是Android的四大組件之中的一個,可見它在Android中的作用非同小可。它基本的作用是:實現各個應用程序之間的(跨應用)數據共享。比方聯系人應用中就使用了ContentProvider,你在自己的應用中能夠讀取和改動聯系人的數據,只是須要獲得對應的權限。事實上它也僅僅是一個中間人,真正的數據源是文件或者SQLite等。
一個應用實現ContentProvider來提供內容給別的應用來操作。 通過ContentResolver來操作別的應用數據,當然在自己的應用中也能夠。
ContentObserver——內容觀察者。目的是觀察(捕捉)特定Uri引起的數據庫的變化。繼而做一些對應的處理。它類似于數據庫技術中的觸發器(Trigger),當ContentObserver所觀察的Uri發生變化時,便會觸發它。觸發器分為表觸發器、行觸發器。對應地ContentObserver也分為“表“ContentObserver、“行”ContentObserver。當然這是與它所監聽的Uri MIME Type有關的。
2、Contacts Demo
1)、基本功能實現
接下來通過一個簡單的存儲聯系人信息的demo,來學習怎么創建自己定義的ContentProvider。這里數據源選用SQLite。最經常使用的也是這個。
(1) 創建一個類NoteContentProvider。繼承ContentProvider。須要實現以下5個方法:
query
insert
update
delete
getType
(2)先來設計一個數據庫,用來聯系人信息,主要包括_ID,name,telephone,create_date,content五個字段。group_name字段等后面升級部分再做使用。
創建ProviderMetaData類,封裝URI和數據庫、表、字段相關信息,源代碼例如以下:
AUTHORITY代表授權,該字符串和在Android描寫敘述文件AndroidManifest.xml中注冊該ContentProvider時的android:authorities值一樣,ContactsData繼承BaseColumns,后者提供了標準的_id字段。表示行ID。
熟悉Content Provider(內容提供者)的應該知道。我們能夠通過UriMatcher類注冊不同類型的Uri,我們能夠通過這些不同的Uri來查詢不同的結果。
依據Uri返回的結果。Uri Type能夠分為:返回多條數據的Uri、返回單條數據的Uri。
Android遵循類似的約定來定義MIME類型,每一個內容類型的Android MIME類型有兩種形式:多條記錄(集合)和單條記錄。
多條記錄
vnd.android.cursor.dir/contact
單條記錄
vnd.android.cursor.item/contact
vnd表示這些類型和子類型具有非標準的、供應商特定的形式。Android中類型已經固定好了。不能更改。僅僅能差別是集合還是單條詳細記錄,子類型/之后的內容能夠依照格式隨便填寫。
在使用Intent時。會用到MIME這玩意。依據Mimetype打開符合條件的活動。
(3) ContentProvider是依據URI來獲取數據的。那它怎么區分不同的URI呢。由于不管是獲取筆記列表還是獲取一條筆記都是調用query方法,如今來實現這個功能。須要用到類UriMatcher,該類能夠幫助我們識別URI類型。以下看實現源代碼:
?
這段代碼是NoteContentProvider類中的。UriMatcher的工作原理:首先須要在UriMatcher中注冊URI模式。每個模式跟一個唯一的編號關聯,注冊之后,在使用中就能夠依據URI得到相應的編號。當模式不匹配時,UriMatcher將返回一個NO_MATCH常量,這樣就能夠區分了。
(4) 還需為查詢設置一個投影映射,主要是將抽象字段映射到數據庫中真實的字段名稱,由于這些字段有時是不同的名稱。既抽象字段的值能夠不跟數據庫中的字段名稱一樣。
這里使用HashMap來完畢,key是抽象字段名稱,value相應數據庫中的字段名稱,只是這里我把兩者的值設置是一樣的,在NoteContentProvider.java中加入如上面所看到的的代碼。
(5) 在NoteContentProvider.java中創建一個內部類DatabaseHelper。繼承自SQLiteOpenHelper,完畢數據庫表的創建、更新,這樣能夠通過它獲得數據庫對象,相關代碼例如以下。
(6) 如今來分別實現第一步中未實現的5個方法,先來實現query方法。這里借助SQLiteQueryBuilder來為查詢設置投影映射以及設置相關查詢條件??丛创a實現:
@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {// TODO Auto-generated method stubSQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();switch(URI_MATCHER.match(uri)){case CONTACTS_ID:queryBuilder.setTables(ContactsData.TABLE_NAME);queryBuilder.setProjectionMap(CONTACTS_PROJECTION_MAP);queryBuilder.appendWhere(ContactsData.TABLE_NAME + "._id="+Long.toString(ContentUris.parseId(uri)));break;case CONTACTS:queryBuilder.setTables(ContactsData.TABLE_NAME);queryBuilder.setProjectionMap(CONTACTS_PROJECTION_MAP);break;}String orderBy;if(TextUtils.isEmpty(sortOrder)){orderBy = ContactsData.DEFAULT_ORDERBY;} else {orderBy = sortOrder;}SQLiteDatabase db = mDbHelper.getReadableDatabase();Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, orderBy);return cursor;}
返回的是一個Cursor對象,它是一個行集合,包括0和多個記錄,類似于JDBC中的ResultSet,能夠前后移動游標。得到每行每列中的數據。注意的是。使用它須要調用moveToFirst(),由于游標默認是在第一行之前。
(7)實現insert方法,實現把記錄插入到基礎數據庫中。然后返回新創建的記錄的URI。
@Overridepublic Uri insert(Uri uri, ContentValues values) {// TODO Auto-generated method stubSQLiteDatabase db = mDbHelper.getWritableDatabase();long id = db.insertOrThrow(ContactsData.TABLE_NAME, null, values);// 更新數據時,通知其它ContentObservergetContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI, null);if(id > 0){return ContentUris.withAppendedId(uri, id);}return null;}
(8) 實現update方法,依據傳入的列值和where字句來更新記錄,返回更新的記錄數,看源代碼:
? @Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = mDbHelper.getWritableDatabase();int modified = 0;switch(URI_MATCHER.match(uri)){case CONTACTS_ID:selection = DatabaseUtils.concatenateWhere(selection,ContactsData.TABLE_NAME + "._id=?");selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,new String[]{Long.toString(ContentUris.parseId(uri))});Log.d("Test", "selectionArgs 0"+selectionArgs);modified = db.update(ContactsData.TABLE_NAME, values, selection, selectionArgs);break;case CONTACTS:modified = db.update(ContactsData.TABLE_NAME, values, selection, selectionArgs);Log.d("Test", "selectionArgs 1"+selectionArgs);break;}// 更新數據時,通知其它ContentObservergetContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI, null);return modified;}
notifyChange函數是在更新數據時,通知其它監聽對象。
(9)實現delete方法,該方法返回刪除的記錄數。
?
"); selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, new String[]{Long.toString(ContentUris.parseId(uri))}); Log.d("Test", "selectionArgs 0"+selectionArgs); deleted = db.delete(ContactsData.TABLE_NAME, selection, selectionArgs); break; case CONTACTS: deleted = db.delete(ContactsData.TABLE_NAME, selection, selectionArgs); Log.d("Test", "selectionArgs 1"+selectionArgs); break; } // 更新數據時,通知其它ContentObserver getContext().getContentResolver().notifyChange(ContactsData.CONTENT_URI, null); return deleted; }
(10) 實現getType方法,依據URI返回MIME類型。這里主要用來區分URI是獲取集合還是單條記錄。這種方法在這里臨時沒啥用處,在使用Intent時實用。
?
(11) 在AndroidManifest.xml中注冊該ContentProvider,這樣系統才找得到,當然你也能夠設置相關的權限。這里就不設置了
? <providerandroid:name="com.johnny.testcontentprovider.ContactsContentProvider"android:authorities="com.johnny.contactsprovider"></provider>
(12)到如今為止。自己定義ContentProvider的所有代碼已經完畢。以下創建一個簡單的應用來測試一下。
主要測試insert、update、delete、query這四個函數。
", new String [] {"James"}, null); Log.e("test ", "count=" + cursor.getCount()); cursor.moveToFirst(); while(!cursor.isAfterLast()) { String name = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_NAME)); String telephone = cursor.getString(cursor.getColumnIndex(ContactsData.CONTACT_TELEPHONE)); long createDate = cursor.getLong(cursor.getColumnIndex(ContactsData.CONTACT_CREATE_DATE)); Log.e("Test", "name: " + name); Log.e("Test", "telephone: " + telephone); Log.e("Test", "date: " + createDate); cursor.moveToNext(); } cursor.close(); }
在插入兩個數據后分別運行updateContact1()和updateContact1ID()函數。你會發現他們兩個分別運行了ContactsContentProvider.update的兩個不同地方:分別為case CONTACTS:和case CONTACTS_ID:,如今能夠理解
(13)創建數據庫監聽器ContentObserver
在MainActivity中增加下面代碼:private ContentObserver mContentObserver = new ContentObserver(new Handler()) {@Overridepublic void onChange(boolean selfChange) {// TODO Auto-generated method stubLog.d("Test", "mContentObserver onChange");super.onChange(selfChange);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (savedInstanceState == null) {getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();}getContentResolver().registerContentObserver(ContactsData.CONTENT_URI, true, mContentObserver);}
每次通過insert、delete、update改變數據庫內容時。都會調用ContentObserver的onChange方法,因此。能夠在這種方法內做出針對數據庫變化的反應。比方更新UI等。
2)、數據庫的升級
當應用公布一段時間之后,我們須要改變數據庫的結構,那么就須要對數據庫的升級了:將DatabaseHelper類中的DATABASE_VERSION設置為2,而且在onUpgrade函數中實現升級的代碼:
? private class DatabaseHelper extends SQLiteOpenHelper{static final String DATABASE_NAME = "test.db";static final int DATABASE_VERSION = 2;public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);// TODO Auto-generated constructor stub}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stubdb.execSQL(ContactsData.SQL_CREATE_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stubLog.d("Test", "onUpgrade oldVersion = "+oldVersion+", newVersion = "+newVersion);//onCreate(db);for(int i = oldVersion+1;i <= newVersion;i++){switch(i){case 2:db.execSQL("ALTER TABLE " + ContactsData.TABLE_NAME + " ADD COLUMN " + ContactsData.CONTACT_GROUP + " TEXT");break;}}}}
也要記得要對onCreate做對應的改動,是用戶清除數據時又一次建表是也會生效
public static final String SQL_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("+ _ID + " INTEGER PRIMARY KEY,"+ CONTACT_NAME + " VARCHAR(50),"+ CONTACT_TELEPHONE + " VARCHAR(11),"+ CONTACT_CONTENT +" TEXT,"+ CONTACT_CREATE_DATE + " INTEGER,"+ CONTACT_GROUP + " TEXT"+ ");" ;以下是升級前后數據庫的結果:
用以下代碼為DATABASE_VERSION = 2的數據庫中的James設在組別和增加Howard聯系人:
結果例如以下:
參考:http://codingnow.cn/android/1078.html
轉載于:https://www.cnblogs.com/wzzkaifa/p/6731611.html
總結
以上是生活随笔為你收集整理的Android ContentProvider、ContentResolver和ContentObserver的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: applicationSettings设
- 下一篇: CS190.1x-ML_lab1_rev