安卓的內(nèi)容提供器是四大組件之一,提供了一種跨應(yīng)用程序訪問(wèn)數(shù)據(jù)的方式,這包含兩方面:(1)在自己的應(yīng)用程序中訪問(wèn)其他應(yīng)用程序的數(shù)據(jù)庫(kù)(例如通過(guò)系統(tǒng)提供的內(nèi)容提供器訪問(wèn)聯(lián)系人、短信、圖片等)。(2)構(gòu)建自己的ContentProvider開(kāi)放自己的數(shù)據(jù)庫(kù)給其他的應(yīng)用程序(或者自己應(yīng)用程序的訪問(wèn))。
和內(nèi)容提供器相關(guān)有三個(gè)重要的類(lèi)ContentResolver,ContentProvider與ContentObserver。
1. ContentResolver
ContentResolver可以通過(guò)Context的getContentResolver()方法取得實(shí)例,每個(gè)應(yīng)用程序在創(chuàng)建時(shí)都會(huì)提供一個(gè)ContentResolver,ContentResolver相當(dāng)于一個(gè)代理,通過(guò)ContentResolver來(lái)對(duì)內(nèi)容提供器進(jìn)行操作,他提供了CURD方法,對(duì)應(yīng)著數(shù)據(jù)庫(kù)中的CURD操作。
ContentResolver通過(guò)Uri來(lái)匹配具體的訪問(wèn)數(shù)據(jù)表。內(nèi)容提供器中的Uri由三部分組成,
第一部分是內(nèi)容提供器的標(biāo)識(shí)content://,第二部分是authority,用來(lái)區(qū)分要訪問(wèn)數(shù)據(jù)庫(kù)所屬的應(yīng)用程序,第三部分是path,標(biāo)識(shí)著要訪問(wèn)的數(shù)據(jù)表或具體的那條數(shù)據(jù)。因此一個(gè)標(biāo)準(zhǔn)的Uri寫(xiě)法如下
content://com.example.app.provider/tablename
一 二 三
*符號(hào)表示匹配任意長(zhǎng)度的任意字符
符號(hào)表示匹配任意長(zhǎng)度的數(shù)字
content://com.example.app.provider/*
表示訪問(wèn)該應(yīng)用程序內(nèi)的任意一張表
content://com.example.app.provider/tablname/#
表示訪問(wèn)tablename表中的任意一行
ContentResolver提供query()、insert()、update()、delete()用來(lái)查詢、增加、更新、刪除數(shù)據(jù)。
query()方法查詢數(shù)據(jù):
Cursor cursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
uri指明查詢的路徑,projection指定要查詢的列,selection與 selectionArgs指定查詢的條件(如Sql中 “where id > 10”),sortOrder指定查詢結(jié)果排序方式。
insert()方法插入數(shù)據(jù):
ContentValues values=new ContentValues();
value.put("name","jack");
value.put("age",10);
getContentResolver.insert(uri,values);
update()方法更新數(shù)據(jù):
ContentValues values=new ContentValues();
values.put("name","macro");
getContentResovler().update(uri,values,"name= ? and age=? " , new String[] {" jack","10"});
delete()方法刪除數(shù)據(jù):
getContentResolver.delete(uri,"name = ?" , new String[] {"macro"});
2. 創(chuàng)建內(nèi)容提供器ContentProvider
自定義內(nèi)容提供器需要重寫(xiě)6個(gè)抽象方法:onCreate(),getType(),query(),insert(),update(),delete(),
一個(gè)基本的自定義內(nèi)容提供器框架如下
public class MyProvider extends ContentProvider {
public static final int TABLE_DIR=0;
public static final int TABLE_ITEM=1;
private static UriMatcher uriMatcher;
static
{
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider","tablename",TABLE_DIR);
uriMatcher.addURI("com.example.app.provider","tablename/#",TABLE_ITEM);
}
@Override
public boolean onCreate() {
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri))
{
case TABLE_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.tablename";
break;
case TABLE_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.tablename";
break;
default:
break;
}
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
//這里填寫(xiě)查詢邏輯
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
//這里填寫(xiě)插入邏輯
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
//這里填寫(xiě)更新邏輯
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
//這里填寫(xiě)刪除邏輯
}
}
TABLE_DIR ,TABLE_ITEM為定義的兩個(gè)常量,分別表示整個(gè)表和表中Item。
UriMatcher是一個(gè)uri匹配器,可以自己設(shè)置authority , path 與code( int 類(lèi)型) 的對(duì)應(yīng)關(guān)系,例如uriMatcher.addURI("com.example.app.provider","tablename",TABLE_DIR)就建立了 authority=com.example.app.provider ,path=tablename 與TABLE_DIR 之間的關(guān)系,通過(guò)uriMather.match("content://com.example.app.provider/tablename")就會(huì)返回TABLE_DIR。
當(dāng)ContentResolver嘗試訪問(wèn)數(shù)據(jù)時(shí)候,內(nèi)容提供器就會(huì)被初始化,onCreate()方法執(zhí)行,返回值代碼是否初始化成功。這里往往添加一些數(shù)據(jù)庫(kù)的創(chuàng)建升級(jí)等操作(例如獲取DatabaseHelper實(shí)例 dbHelper=new MyDatabaseHelper(。。。) )。
query()、insert()、update()、delete()中用來(lái)處理根據(jù)提供的uri與其他參數(shù)來(lái)操作數(shù)據(jù)庫(kù)的實(shí)際邏輯,例如查詢數(shù)據(jù)庫(kù)、插入數(shù)據(jù)等。
getType()方法會(huì)根據(jù)Uri返回對(duì)應(yīng)MIME類(lèi)型。MIME字符串也由三部分組成:
1.必須以vnd開(kāi)頭
2.如果URI以表名結(jié)尾,vnd后面接android.cursor.dir,以表中某一id(item)結(jié)果,vnd后面接android.cursor.item。
3.然后在最后接上vnd.<authority>.<path>。
例如
vnd.android.cursor.dir/vnd.com.example.app.provider.table
表示訪問(wèn)表
vnd.android.cursor.item/vnd.com.example.app.provider.table
表示訪問(wèn)表中某一item
內(nèi)容提供器是四大組件之一,因此需要在AndroidManifest.xml文件中配置一下。
<provider
android:name=".MyProvider"
android:authorities="com.example.app.provider"
android:enable="true"
android:exported="true"
>
</provider>
enable表示是否啟用這個(gè)內(nèi)容提供器,exported表示是否允許其他應(yīng)用程序訪問(wèn)這個(gè)內(nèi)容提供器。
3. ContentObserver內(nèi)容監(jiān)聽(tīng)者
某些時(shí)候數(shù)據(jù)庫(kù)發(fā)生改變(例如新建了聯(lián)系人,從網(wǎng)絡(luò)刷新了新數(shù)據(jù)、或者來(lái)短信了),我們不能及時(shí)知道就導(dǎo)致已有的數(shù)據(jù)成為舊數(shù)據(jù),然而通過(guò)query()方法再去查詢需要主要去操作或者另起線程定期查詢這樣效率低而且很被動(dòng),這時(shí)候就可以使用ContentObserver,ContentObserver使用觀察者模式,當(dāng)數(shù)據(jù)庫(kù)改變時(shí)候通知注冊(cè)的observer實(shí)例對(duì)象執(zhí)行更新操作。
創(chuàng)建自己的內(nèi)容觀察者對(duì)象:
private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
//這里執(zhí)行更新邏輯;
}
}
};
繼承ContentObserver類(lèi)需要重寫(xiě)onChange()方法,當(dāng)數(shù)據(jù)庫(kù)發(fā)生改變時(shí)候會(huì)回調(diào)這個(gè)onChange()方法。聲明ContentObserver實(shí)例對(duì)象時(shí)候需要傳入一個(gè)Handler()對(duì)象。
通過(guò)ContentResolver來(lái)注冊(cè)和取消注冊(cè)這個(gè)觀察者
//注冊(cè)觀測(cè)者 第一個(gè)參數(shù)為監(jiān)聽(tīng)的uri, 第二個(gè)參數(shù)表示是否應(yīng)用到子Uri,如果為true表示對(duì)派生Uri依然有效。
getContentResolver().registerContentObserver(uri, true, mContentObserver);
//注銷(xiāo)觀察者
getContentResolver().unregisterContentObserver(mContentObserver);