日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android ContentProvider、ContentResolver和ContentObserver的使用

發布時間:2025/3/21 Android 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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

public class ContactsContentProvider extends ContentProvider{@Overridepublic boolean onCreate() {// TODO Auto-generated method stubreturn false;}@Overridepublic int delete(Uri arg0, String arg1, String[] arg2) {// TODO Auto-generated method stubreturn 0;}@Overridepublic String getType(Uri arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic Uri insert(Uri arg0, ContentValues arg1) {// TODO Auto-generated method stubreturn null;}@Overridepublic Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,String arg4) {// TODO Auto-generated method stubreturn null;}@Overridepublic int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {// TODO Auto-generated method stubreturn 0;}}


(2)先來設計一個數據庫,用來聯系人信息,主要包括_ID,name,telephone,create_date,content五個字段。group_name字段等后面升級部分再做使用。

創建ProviderMetaData類,封裝URI和數據庫、表、字段相關信息,源代碼例如以下:

public class ProviderMetaData {public static final String AUTHORITY = "com.johnny.contactsprovider";public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);public static final class ContactsData implements BaseColumns{public static final String TABLE_NAME = "contacts";public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, TABLE_NAME);public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact";public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact";public static final String CONTACT_NAME = "name";public static final String CONTACT_TELEPHONE = "telephone";public static final String CONTACT_CREATE_DATE = "create_date";public static final String CONTACT_CONTENT = "content";public static final String CONTACT_GROUP = "group_name";public static final String DEFAULT_ORDERBY = "create_date DESC";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"+ ");" ;} }

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類型。以下看實現源代碼:
?

static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);static final HashMap<String, String> CONTACTS_PROJECTION_MAP = new HashMap<String, String>();private static final int CONTACTS = 1;private static final int CONTACTS_ID = 2;static{final UriMatcher matcher = URI_MATCHER;matcher.addURI(ProviderMetaData.AUTHORITY, "contacts", CONTACTS);matcher.addURI(ProviderMetaData.AUTHORITY, "contacts/#", CONTACTS_ID);HashMap<String, String> map = CONTACTS_PROJECTION_MAP;map.put(ContactsData._ID, ContactsData._ID);map.put(ContactsData.CONTACT_NAME, ContactsData.CONTACT_NAME);map.put(ContactsData.CONTACT_TELEPHONE, ContactsData.CONTACT_TELEPHONE);map.put(ContactsData.CONTACT_CONTENT, ContactsData.CONTACT_CONTENT);map.put(ContactsData.CONTACT_CREATE_DATE, ContactsData.CONTACT_CREATE_DATE);}

這段代碼是NoteContentProvider類中的。UriMatcher的工作原理:首先須要在UriMatcher中注冊URI模式。每個模式跟一個唯一的編號關聯,注冊之后,在使用中就能夠依據URI得到相應的編號。當模式不匹配時,UriMatcher將返回一個NO_MATCH常量,這樣就能夠區分了。


(4) 還需為查詢設置一個投影映射,主要是將抽象字段映射到數據庫中真實的字段名稱,由于這些字段有時是不同的名稱。既抽象字段的值能夠不跟數據庫中的字段名稱一樣。

這里使用HashMap來完畢,key是抽象字段名稱,value相應數據庫中的字段名稱,只是這里我把兩者的值設置是一樣的,在NoteContentProvider.java中加入如上面所看到的的代碼。


(5) 在NoteContentProvider.java中創建一個內部類DatabaseHelper。繼承自SQLiteOpenHelper,完畢數據庫表的創建、更新,這樣能夠通過它獲得數據庫對象,相關代碼例如以下。


private class DatabaseHelper extends SQLiteOpenHelper{static final String DATABASE_NAME = "test.db";static final int DATABASE_VERSION = 1;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 stubonCreate(db);}}

(6) 如今來分別實現第一步中未實現的5個方法,先來實現query方法。這里借助SQLiteQueryBuilder來為查詢設置投影映射以及設置相關查詢條件。看源代碼實現:

@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方法,該方法返回刪除的記錄數。


?

@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = mDbHelper.getWritableDatabase();int deleted = 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); 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時實用。


?

@Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch(URI_MATCHER.match(uri)){case CONTACTS:return ContactsData.CONTENT_TYPE;case CONTACTS_ID:return ContactsData.CONTENT_ITEM_TYPE; // default: // throw new IllegalArgumentException("Unknow URI: " + uri);}return null;}

(11) 在AndroidManifest.xml中注冊該ContentProvider,這樣系統才找得到,當然你也能夠設置相關的權限。這里就不設置了
? <providerandroid:name="com.johnny.testcontentprovider.ContactsContentProvider"android:authorities="com.johnny.contactsprovider"></provider>

(12)到如今為止。自己定義ContentProvider的所有代碼已經完畢。以下創建一個簡單的應用來測試一下。
主要測試insert、update、delete、query這四個函數。


private void insertContact1(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_NAME, "James");values.put(ContactsData.CONTACT_TELEPHONE, "18888888888");values.put(ContactsData.CONTACT_CONTENT, "NBA Star");values.put(ContactsData.CONTACT_CREATE_DATE, System.currentTimeMillis());Uri uri = getContentResolver().insert(ContactsData.CONTENT_URI, values);Log.d("Test", "uri = "+uri);}private void deleteContact1(){int count = getContentResolver().delete(ContactsData.CONTENT_URI, ContactsData.CONTACT_NAME+"='James'", null);Log.d("Test", "count = "+count);}private void updateContact1(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_TELEPHONE, "16666666666");int count = getContentResolver().update(ContactsData.CONTENT_URI,values, ContactsData.CONTACT_NAME+"='James'", null);Log.d("Test", "count = "+count);}private void updateContact1ID(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_TELEPHONE, "17777777");int count = getContentResolver().update(ContentUris.withAppendedId(ContactsData.CONTENT_URI,2),values, null, null);Log.d("Test", "count = "+count);}private void queryContact1(){//Cursor cursor = this.getContentResolver().query(ContactsData.CONTENT_URI, null, ContactsData.CONTACT_NAME+"='James'", null, null);Cursor cursor = this.getContentResolver().query(ContactsData.CONTENT_URI, null, ContactsData.CONTACT_NAME+"=?

", 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:,如今能夠理解

matcher.addURI(ProviderMetaData.AUTHORITY, "contacts", CONTACTS);matcher.addURI(ProviderMetaData.AUTHORITY, "contacts/#", 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聯系人:

private void modifyContact1(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_GROUP, "Miami");int count = getContentResolver().update(ContactsData.CONTENT_URI,values, ContactsData.CONTACT_NAME+"='James'", null);Log.d("Test", "count = "+count);}private void insertContact2(){ContentValues values = new ContentValues();values.put(ContactsData.CONTACT_NAME, "Howard");values.put(ContactsData.CONTACT_TELEPHONE, "13333333333");values.put(ContactsData.CONTACT_CONTENT, "NBA Star");values.put(ContactsData.CONTACT_GROUP, "Rockets");values.put(ContactsData.CONTACT_CREATE_DATE, System.currentTimeMillis());Uri uri = getContentResolver().insert(ContactsData.CONTENT_URI, values);Log.d("Test", "uri = "+uri);}private void queryContacts1(){Cursor cursor = this.getContentResolver().query(ContactsData.CONTENT_URI, null, ContactsData.CONTACT_GROUP+" IN ('Miami','Rockets')", null, 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();}
結果例如以下:



參考:http://codingnow.cn/android/1078.html

轉載于:https://www.cnblogs.com/wzzkaifa/p/6731611.html

總結

以上是生活随笔為你收集整理的Android ContentProvider、ContentResolver和ContentObserver的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。