ContentProvider的意義
ContentProvider的類注釋如下:
Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via android.database.sqlite.SQLiteDatabase
內(nèi)容提供者是Android應(yīng)用程序的主要構(gòu)建塊之一呻拌,為應(yīng)用程序提供內(nèi)容。它們封裝數(shù)據(jù)并通過單一的ContentResolver接口提供給應(yīng)用程序凝果。如果需要在多個應(yīng)用程序之間共享數(shù)據(jù)耘拇,則只需要內(nèi)容提供程序。例如污它,聯(lián)系人數(shù)據(jù)由多個應(yīng)用程序使用剖踊,必須存儲在內(nèi)容提供者中。如果您不需要在多個應(yīng)用程序之間共享數(shù)據(jù)衫贬,您可以直接通過數(shù)據(jù)庫使用數(shù)據(jù)庫德澈。
由此可見,ContentProvider適合進(jìn)程間的數(shù)據(jù)共享固惯,你的應(yīng)用可以通過ContentProvider將需要共享的部分?jǐn)?shù)據(jù)提供給其他應(yīng)用梆造。你也可以通過ContentProvider獲取手機(jī)的圖片、音視頻葬毫、通訊錄等數(shù)據(jù)
ContentProvider實(shí)現(xiàn)
需要重寫以下的幾個主要方法
- onCreate 初始化創(chuàng)建ContentProvider
- query 返回查詢數(shù)據(jù)
- insert 插入數(shù)據(jù)
- update 更新數(shù)據(jù)
- delete 刪除數(shù)據(jù)
- getType
getType方法很特別镇辉,基本用不上,但是它具體的作用又是什么呢贴捡,來看看它的注釋
Implement this to handle requests for the MIME type of the data at the
given URI. The returned MIME type should start with vnd.android.cursor.item for a single record, or vnd.android.cursor.dir/ for multiple items.
根據(jù)訪問的URI忽肛,返回對應(yīng)的MIME類型的字符串,這個字符串需要以vnd.android.cursor.item(返回單條數(shù)據(jù)記錄)或者vnd.android.cursor.dir/(返回多條數(shù)據(jù)記錄) 開頭
看到這些方法是不是很熟悉烂斋?跟數(shù)據(jù)庫操作一毛一樣啊屹逛,其實(shí)在這個時候我們可以將ContentProvider理解成一個共享數(shù)據(jù)庫础废,我們只要在ContentProvider初始化的時候,實(shí)例化一個可讀寫的數(shù)據(jù)庫罕模,在以上的方法中對這個數(shù)據(jù)庫進(jìn)行增刪改查的操作就可以了评腺。當(dāng)你的數(shù)據(jù)庫有多個表的時候,這些增刪改查又是如何區(qū)分不同的表呢淑掌?其實(shí)這些方法都有一個Uri的參數(shù)回調(diào)蒿讥,我們可以通過實(shí)例UriMatcher來區(qū)分用戶操作的到底是哪個表。
示例
創(chuàng)建一個DBOpenHelper
public static class TestDBOpenHelper extends SQLiteOpenHelper{
public static final String DB_NAME = "test.db";
public static final int VERSION=1;
public static final String TABLE_NAME = "person";
public static final String TABLE_NAME2 = "class";
public static final String CREATE_TABLE = "create table if not exists "+TABLE_NAME+"(_id integer, name TEXT, desc TEXT)";
public static final String CREATE_TABLE2= "create table if not exists "+TABLE_NAME2+"(id integer, className TEXT, desc TEXT)";
public TestDBOpenHelper(Context context){
super(context,DB_NAME,null,VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
db.execSQL(CREATE_TABLE2);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
ContentProvider
public class TestContentProvider extends ContentProvider {
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
public static final String AUTHORITY = "com.gzkit.dailysample.TestContentProvider"; //授權(quán)
public static final Uri PERSON_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/person");
public static final Uri CLASS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/class");
private static final int TABLE_CODE_PERSON = 2;
private static final int TABLE_CODE_CLASS = 3;
static {
//關(guān)聯(lián)不同的 URI 和 code抛腕,便于后續(xù) getType
mUriMatcher.addURI(AUTHORITY, "person", TABLE_CODE_PERSON);
mUriMatcher.addURI(AUTHORITY, "class", TABLE_CODE_CLASS);
}
//數(shù)據(jù)庫
private SQLiteDatabase mDatabase;
private Context mContext;
private String mTable;
private void initProvider(){
mContext = getContext();
mTable = TestDBOpenHelper.TABLE_NAME;
mDatabase = new TestDBOpenHelper(mContext).getWritableDatabase();
//插入一條初始數(shù)據(jù)
mDatabase.execSQL("insert into "+mTable+" values(1,'test','good boy') ");
}
@Override
public boolean onCreate() {
initProvider();
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
String tableName = getTableName(uri);
return mDatabase.query(tableName,projection,selection,selectionArgs,null,sortOrder,null);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
int match = mUriMatcher.match(uri);
String MIME = "";
switch (match) {
case TABLE_CODE_PERSON:
MIME = "vnd.android.cursor.dir/multi";
break;
case TABLE_CODE_CLASS:
MIME = "vnd.android.cursor.item/single";
break;
}
return MIME;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
String tableName = getTableName(uri);
mDatabase.insert(tableName,null,values);
//發(fā)送數(shù)據(jù)變動的通知
mContext.getContentResolver().notifyChange(uri,null);
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
/**
* CRUD 的參數(shù)是 Uri芋绸,根據(jù) Uri 獲取對應(yīng)的表名
*
* @param uri
* @return
*/
private String getTableName(final Uri uri) {
String tableName = "";
int match = mUriMatcher.match(uri);
switch (match){
case TABLE_CODE_PERSON:
tableName = TestDBOpenHelper.TABLE_NAME;
break;
case TABLE_CODE_CLASS:
tableName = TestDBOpenHelper.TABLE_NAME2;
break;
}
return tableName;
}
在AndroidManifest中注冊ContentProvider
提供ContentProvider的APP按實(shí)際需要加入以下權(quán)限
<permission android:name="com.gzkit.dailysample.permission.READ_CONTENT"
android:protectionLevel="normal"
/>
<permission android:name="com.gzkit.dailysample.permission.WRITE_CONTENT"
android:protectionLevel="normal"
/>
<permission android:name="com.gzkit.dailysample.provider.permission"
android:protectionLevel="normal"
/>
如果provider不存在這些權(quán)限的屬性,聲明這些權(quán)限則不是必要的
application節(jié)點(diǎn)下
<provider
android:name="com.gzkit.dailysample.bean.TestContentProvider"
android:authorities="com.gzkit.dailysample.TestContentProvider"
android:exported="true"
android:readPermission="com.gzkit.dailysample.permission.READ_CONTENT"
android:writePermission="com.gzkit.dailysample.permission.WRITE_CONTENT"
android:permission="com.gzkit.dailysample.provider.permission"
android:process=":provider"
/>
- name 自定義的類
- authorities 定義授權(quán)的名稱兽埃,在自定義的ContentProvider中要用到
- exported 外部是否能調(diào)用
- process 進(jìn)程名
- readPermission 讀取的權(quán)限名侥钳,必須和前面的permission節(jié)點(diǎn)名一致
- writePermission 寫入的權(quán)限名,必須和前面的permission節(jié)點(diǎn)名一致
- permission 讀寫的權(quán)限名柄错,必須和前面的permission節(jié)點(diǎn)名一致
值得注意的是舷夺,readPermission和writePermission的優(yōu)先級比permission高。
有以下幾種情況
- 所有權(quán)限都不聲明售貌,其他應(yīng)用無須聲明權(quán)限就可以自由讀寫
- 只聲明了permission给猾,則其他應(yīng)用需要聲明對應(yīng)的權(quán)限,即可讀寫
- 只聲明readPermission颂跨,則其他應(yīng)用需要聲明此權(quán)限敢伸,才可以讀取數(shù)據(jù),但仍然可以寫入數(shù)據(jù)
- 只聲明writePermission恒削,和上面相反
- 聲明readPermission或writePermission池颈,同時聲明了permission,則其他應(yīng)用需要聲明對應(yīng)的權(quán)限钓丰,可以讀寫躯砰。
ContentProvider聲明了哪種權(quán)限,調(diào)用的那方就必須聲明對應(yīng)的權(quán)限
調(diào)用方需要聲明對應(yīng)的權(quán)限
<!--ContentProvider需要的權(quán)限-->
<uses-permission android:name="com.gzkit.dailysample.permission.READ_CONTENT"/>
<uses-permission android:name="com.gzkit.dailysample.permission.WRITE_CONTENT"/>
<uses-permission android:name="com.gzkit.dailysample.provider.permission"/>
使用ContentProvider
在activity或service中通過getContentResolver()獲取resolver接口進(jìn)行數(shù)據(jù)的增刪查改
public static final String AUTHORITY = "com.gzkit.dailysample.TestContentProvider"; //授權(quán)
public static final Uri PERSON_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/person");
public static final Uri CLASS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/class");
public void insert(){
ContentValues contentValues = new ContentValues();
contentValues.put("_id",1);
contentValues.put("name","test here");
contentValues.put("desc","here is desc");
getContentResolver().insert(PERSON_CONTENT_URI,contentValues);
}
public void query(){
Cursor cursor = getContentResolver().query(CLASS_CONTENT_URI,null,null,null,null);
Toast.makeText(mContext, "查詢結(jié)果數(shù):"+cursor.getCount(), Toast.LENGTH_SHORT).show();
cursor.close();
}
下一篇携丁,將會講到如何利用ContentProvider讀取手機(jī)的多媒體資料