參考:
一. 用途:
- 跨程序共享數(shù)據(jù)(為其他應(yīng)用程序提供訪問數(shù)據(jù)的接口)
- 利用Content Provider對數(shù)據(jù)進(jìn)行封裝高镐,有利于脫離對數(shù)據(jù)庫的依賴性(解耦)鸠真。改變底層數(shù)據(jù)庫野来,而上層數(shù)據(jù)查詢不用改變。
二. 用到的各種類
用來訪問ContentProvider的類庭砍,提供了和ContentProvider類中方法同名的方法,Content Provider只是用來提供數(shù)據(jù)的矮锈,而訪問就需要用Content Resolver豺旬。
內(nèi)容提供器钠惩,可以用來給其他程序提供數(shù)據(jù),也可以充當(dāng)其數(shù)據(jù)存儲區(qū)和表格形式的數(shù)據(jù)外部顯示之間的抽象層族阅。主要用來提供對外訪問的接口(CRUD)篓跛。
用來區(qū)分應(yīng)用程序、字段的URI坦刀,與RUL類似愧沟,分為一下三個(gè)部分,要想使用Content Provider提供的數(shù)據(jù)求泰,就必須通過Uri進(jìn)行訪問央渣,而且只能通過在ContentProvider中已經(jīng)添加(調(diào)用UriMatcher.addURI添加的)的Uri訪問,未添加的就不能訪問渴频。
- schema:協(xié)議部分芽丹,Content Provider默認(rèn)協(xié)議是content://
- authority:權(quán)限部分,其實(shí)就是用來區(qū)分是哪個(gè)Content Provider卜朗,通常是應(yīng)用的包名
- path:用來區(qū)分?jǐn)?shù)據(jù)的路徑拔第,一般是表名,有以下形式:
- 訪問多條數(shù)據(jù)(整個(gè)表)com.example.provider/table
- 訪問某一行(rowId對應(yīng)的行)com.example.provider/table/rowId
- PS:Uri的作用有很多场钉,不僅僅是用在這里蚊俺,還有如用瀏覽器打開鏈接的時(shí)候會用到
-
Uri.Builder
- 用來連綴構(gòu)建標(biāo)準(zhǔn)Uri的類(調(diào)用各種方法進(jìn)行參數(shù)的連綴),有如下常用方法:(其實(shí)這些方法很多都是重復(fù)的功能)
Uri.Builder scheme(String scheme)//給Uri添加schema參數(shù)
Uri.Builder authority(String authority)//給Uri添加authority參數(shù)
Uri.Builder path(String path)//給Uri添加path參數(shù)
Uri.Builder query(String query)//給Uri添加id參數(shù)(查詢參數(shù))
Uri build()//生成Uri
Uri.Builder appendPath(String newSegment)//添加path參數(shù)
- 用來給Uri追加id和讀取Uri中的id的幫助類逛万,有如下方法:
static Uri.Builder appendId(Uri.Builder builder, long id)//給Uri.Builder要構(gòu)建的Uri添加id
static long parseId(Uri contentUri)//解析Uri中的id
static Uri withAppendedId(Uri contentUri, long id)//給Uri添加d
-
UriMatcher
- Content Provider用來處理(匹配)不同Uri的類泳猬,創(chuàng)建Content Provider的時(shí)候就依靠UriMatcher進(jìn)行匹配從而確定對不同的Uri請求進(jìn)行不同的操作,有以下兩個(gè)方法:
/**
* 添加一個(gè)要匹配的Uri宇植,可以用通配符得封,其中*用來通配一個(gè)字符,#用來匹配任何數(shù)字指郁。
*
* @param authority Uri的authority
* @param 要匹配的路徑忙上,可以用通配符
* @param 匹配成功時(shí)返回的數(shù)字,根據(jù)該數(shù)字進(jìn)行判斷和匹配
*/
public void addURI(String authority, String path, int code)
/**
* 進(jìn)行匹配的方法
*
* @param uri 要匹配的Uri
*
* @return 返回值闲坎,匹配到時(shí)返回addURI里面的第三個(gè)參數(shù)疫粥,匹配失敳绺(Content Provider未提供該uri)的時(shí)候返回-1
*/
public int match(Uri uri)
- Uri的MIME類型(不知道是干嘛的,原諒我還沒學(xué)計(jì)算機(jī)網(wǎng)絡(luò)QAQ)
- 構(gòu)成方法:
- 以vnd開頭
- 如URI以路徑結(jié)尾梗逮,則接android.cursor.dir项秉,如果以id結(jié)尾,則接android.cursor.item
- 接著再接/vnd.<authority>.<path>
- 如:
- 構(gòu)成方法:
vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book//路徑結(jié)尾
vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book//id結(jié)尾
三. 訪問Content Provider提供的數(shù)據(jù)的步驟
- 構(gòu)建要訪問的Uri
- 調(diào)用Uri.parse()將String轉(zhuǎn)化為Uri
- 調(diào)用Uri.Builder方法進(jìn)行各種連綴構(gòu)造(其實(shí)可以不用調(diào)用這些方法库糠,直接寫好String然后parse成Uri就可以了伙狐,但是推薦做法是調(diào)用API中的函數(shù)進(jìn)行構(gòu)造)涮毫,一般有如下方式構(gòu)建Uri:(構(gòu)建Uri的方法很多瞬欧,這里只是兩種)
Uri uri = new Uri.Builder().scheme("content").authority("cn.foxnickel.databasedemo.provider").path("table1").build();//用Builder進(jìn)行構(gòu)造
Uri uri1 = Uri.parse("content://cn.foxnickel.databasedemo.provider/table/id");//用String構(gòu)建Uri
- 得到Content Resolver并調(diào)用CRUD方法進(jìn)行數(shù)據(jù)的操作
- 得到Content Resolver(Context提供了getContentResolver方法用來得到Content Resolver)
ContentResolver contentResolver = Context.getContentResolver();
- 調(diào)用Content Resolver的CRUD方法進(jìn)行數(shù)據(jù)操作,和SQLiteDatabase的CRUD方法一樣罢防。
三. 訪問系統(tǒng)提供的Content Provider
- 訪問系統(tǒng)提供的Content Provider的時(shí)候就不需要自己構(gòu)建Uri艘虎,因?yàn)橄到y(tǒng)對各種Uri都是封裝好了的,只需要查找官方文檔就可以使用了
-
常見的內(nèi)置Content Provider
- 聯(lián)系人提供程序
- 日歷提供查詢
- 短信提供程序
- 用戶字典提供程序
- 媒體提供程序
四. 創(chuàng)建自己應(yīng)用的Content Provider
- 實(shí)現(xiàn)自己的Content Provider類咒吐,繼承自Content Provider基類
- 實(shí)現(xiàn)六個(gè)必須的抽象方法(onCreate野建,getType,insert恬叹,delete候生,update,query)
- 實(shí)例化UriMatcher绽昼,添加要提供的Uri
- 根據(jù)添加的Uri進(jìn)行判斷唯鸭,然后具體實(shí)現(xiàn)六個(gè)抽象方法
/**
* 該類是ContentProvider的實(shí)現(xiàn)類,實(shí)現(xiàn)了對數(shù)據(jù)庫數(shù)據(jù)的共享
*/
public class DatabaseProvider extends ContentProvider {
/**
* 定義支持的Uri的匹配號
*/
private static final int BOOK_DIR = 0;
private static final int BOOK_ITEM = 1;
private static final int CATEGORY_DIR = 2;
private static final int CATEGORY_ITEM = 3;
private static final String AUTHORITY = "cn.foxnickel.databasedemo.provider";
private static UriMatcher mUriMatcher;//靜態(tài)的UriMatcher
private MyDatabaseHelper mDatabaseHelper;//用來查詢數(shù)據(jù)庫數(shù)據(jù)的Helper
private final String TAG = getClass().getSimpleName();
private SQLiteDatabase db;//數(shù)據(jù)庫
/**
* 在靜態(tài)代碼塊中進(jìn)行Uri的添加
*/
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
mUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
mUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
mUriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
/**
* delete方法:刪除數(shù)據(jù)庫中的數(shù)據(jù)
* @param uri 指定表或者行的Uri
* @param selection 刪除的條件
* @param selectionArgs selection中占位符的值
* @return 返回刪除的某行的行號
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i(TAG, "content provider delete:" + uri);
int deletedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("book",selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("book","id = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("category",selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("category","id = ?",new String[]{categoryId});
break;
default:
break;
}
return deletedRows;
}
/**
* 返回傳入U(xiǎn)ri的MIME類型
* @param uri ContentResolver傳來的Uri
* @return 該Uri對應(yīng)的MIME類型
*/
@Override
public String getType(Uri uri) {
Log.i(TAG, "content provider getType:" + uri);
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.category";
default:
break;
}
return null;
}
/**
* 插入數(shù)據(jù)
* @param uri 指定表或者行的Uri
* @param values 包裝了要插入數(shù)據(jù)的ContentValues
* @return 用于表示新紀(jì)錄的Uri
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "content provider insert:" + uri);
Uri uriReturn = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("book",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("category",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
break;
default:
break;
}
return uriReturn;
}
/**
* 當(dāng)ContentProvider第一次被調(diào)用的時(shí)候會調(diào)用onCreate方法硅确,一般在這里進(jìn)行數(shù)據(jù)庫的初始化
* @return 是否初始化成功
*/
@Override
public boolean onCreate() {
Log.i(TAG, "content provider onCreate: ");
mDatabaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
db = mDatabaseHelper.getWritableDatabase();
return true;
}
/**
* 查詢數(shù)據(jù)
* @param uri 指定要查詢的表/列的Uri
* @param projection 要查詢的列
* @param selection where條件
* @param selectionArgs selection條件中占位符的值
* @param sortOrder 排序方式目溉,默認(rèn)為升序
* @return 查詢到的游標(biāo)
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
Log.i(TAG, "content provider query:" + uri);
cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
/**
* 更新數(shù)據(jù)
* @param uri 指定要更新的表/列的Uri
* @param values 包裝了要更新數(shù)據(jù)的ContentValues
* @param selection where條件
* @param selectionArgs selection條件中占位符的值
* @return 更新的行號
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i(TAG, "content provider update:" + uri);
int updatedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("book",values,selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("category",values,selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{categoryId});
break;
default:
break;
}
return updatedRows;
}
}
- manifest進(jìn)行聲明
<!--
name:Content Provider的名字
authorities:權(quán)限(包名)
enable:是否啟用
exported:是否能被其他軟件使用
-->
<provider
android:name=".data.DatabaseProvider"
android:authorities="cn.foxnickel.databasedemo.provider"
android:enabled="true"
android:exported="true">
</provider>
- 創(chuàng)建完成之后就可以通過三中的調(diào)用方法進(jìn)行調(diào)用了
五. 源碼
Provider類
/**
* 該類是ContentProvider的實(shí)現(xiàn)類,實(shí)現(xiàn)了對數(shù)據(jù)庫數(shù)據(jù)的共享
*/
public class DatabaseProvider extends ContentProvider {
/**
* 定義支持的Uri的匹配號
*/
private static final int BOOK_DIR = 0;
private static final int BOOK_ITEM = 1;
private static final int CATEGORY_DIR = 2;
private static final int CATEGORY_ITEM = 3;
private static final String AUTHORITY = "cn.foxnickel.databasedemo.provider";
private static UriMatcher mUriMatcher;//靜態(tài)的UriMatcher
private MyDatabaseHelper mDatabaseHelper;//用來查詢數(shù)據(jù)庫數(shù)據(jù)的Helper
private final String TAG = getClass().getSimpleName();
private SQLiteDatabase db;//數(shù)據(jù)庫
/**
* 在靜態(tài)代碼塊中進(jìn)行Uri的添加
*/
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
mUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
mUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
mUriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
/**
* delete方法:刪除數(shù)據(jù)庫中的數(shù)據(jù)
* @param uri 指定表或者行的Uri
* @param selection 刪除的條件
* @param selectionArgs selection中占位符的值
* @return 返回刪除的某行的行號
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i(TAG, "content provider delete:" + uri);
int deletedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("book",selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("book","id = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("category",selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("category","id = ?",new String[]{categoryId});
break;
default:
break;
}
return deletedRows;
}
/**
* 返回傳入U(xiǎn)ri的MIME類型
* @param uri ContentResolver傳來的Uri
* @return 該Uri對應(yīng)的MIME類型
*/
@Override
public String getType(Uri uri) {
Log.i(TAG, "content provider getType:" + uri);
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.category";
default:
break;
}
return null;
}
/**
* 插入數(shù)據(jù)
* @param uri 指定表或者行的Uri
* @param values 包裝了要插入數(shù)據(jù)的ContentValues
* @return 用于表示新紀(jì)錄的Uri
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "content provider insert:" + uri);
Uri uriReturn = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("book",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("category",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
break;
default:
break;
}
return uriReturn;
}
/**
* 當(dāng)ContentProvider第一次被調(diào)用的時(shí)候會調(diào)用onCreate方法菱农,一般在這里進(jìn)行數(shù)據(jù)庫的初始化
* @return 是否初始化成功
*/
@Override
public boolean onCreate() {
Log.i(TAG, "content provider onCreate: ");
mDatabaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
db = mDatabaseHelper.getWritableDatabase();
return true;
}
/**
* 查詢數(shù)據(jù)
* @param uri 指定要查詢的表/列的Uri
* @param projection 要查詢的列
* @param selection where條件
* @param selectionArgs selection條件中占位符的值
* @param sortOrder 排序方式缭付,默認(rèn)為升序
* @return 查詢到的游標(biāo)
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
Log.i(TAG, "content provider query:" + uri);
cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
/**
* 更新數(shù)據(jù)
* @param uri 指定要更新的表/列的Uri
* @param values 包裝了要更新數(shù)據(jù)的ContentValues
* @param selection where條件
* @param selectionArgs selection條件中占位符的值
* @return 更新的行號
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i(TAG, "content provider update:" + uri);
int updatedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("book",values,selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("category",values,selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{categoryId});
break;
default:
break;
}
return updatedRows;
}
}
調(diào)用Provider的類
public void onClick(View v) {
ContentResolver contentResolver = getContentResolver();
switch (v.getId()) {
case R.id.add_data:
ContentValues values = new ContentValues();
values.put("name","A Clash of Kings");
values.put("author","George Martin");
values.put("pages",1040);
values.put("price",22.85);
Uri newUri = contentResolver.insert(Uri.parse(AUTHORITY+"/book"),values);
newId = newUri.getPathSegments().get(1);
break;
case R.id.query_data:
Cursor cursor = null;
cursor = contentResolver.query(Uri.parse(AUTHORITY+"/book"),null,null,null,null);
if(cursor!=null){
while (cursor.moveToNext()){
Log.i(TAG, "onClick: name: "+cursor.getString(cursor.getColumnIndex("name")));
Log.i(TAG, "onClick: author: "+cursor.getString(cursor.getColumnIndex("author")));
Log.i(TAG, "onClick: price: "+cursor.getFloat(cursor.getColumnIndex("price")));
Log.i(TAG, "onClick: pages: "+cursor.getString(cursor.getColumnIndex("pages")));
}
cursor.close();
}
break;
case R.id.update_data:
ContentValues newValues = new ContentValues();
newValues.put("name","A Storm of sWords");
contentResolver.update(Uri.parse(AUTHORITY+"/book"),newValues,"pages = ?",new String[]{"1040"});
break;
case R.id.delete_data:
contentResolver.delete(Uri.parse(AUTHORITY+"/book/"+1),null,null);
break;
default:
break;
}
}