Android ContentProvider介绍
在Android中數(shù)據(jù)的存儲(chǔ)一共有五種形式,分別是:Shared Preferences、網(wǎng)絡(luò)存儲(chǔ)、文件存儲(chǔ),外儲(chǔ)存儲(chǔ)、SQLite。但是我們知道一般這些存儲(chǔ)都只是在單獨(dú)的一個(gè)應(yīng)用程序之中達(dá)到一個(gè)數(shù)據(jù)的共享。而使用ContentProvider共享數(shù)據(jù)的好處是統(tǒng)一了數(shù)據(jù)訪問(wèn)方式。ContentProvide對(duì)數(shù)據(jù)進(jìn)行封裝,不用關(guān)心數(shù)據(jù)存儲(chǔ)的細(xì)節(jié)。下面我們通過(guò)代碼來(lái)介紹這個(gè)ContentProvider的使用過(guò)程。
public class ItemContentProvier {/*Authority*/ public static final String AUTHORITY="com.itchq.contentprovider";/*Match Code*/public static final int ITEM=1;public static final int ITEM_POS=2;/*Default sort order*/public static final String DEFAULT_SORT_ORDER = "_id asc";/*MIME*/ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";public static final String ARTICE_ITEM="articeitem";/*Content URI*/public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);public interface Artice{public static final String ID="_id";public static final String TITLE="title";public static final String CONTENT_ID="contentid";}}上面這個(gè)類都是定義數(shù)據(jù)庫(kù)一些常量,我們往往把它單獨(dú)的提前出來(lái)使代碼結(jié)構(gòu)更清晰一點(diǎn)。
URI 通用資源標(biāo)志符,這個(gè)Uri代表了要操作的數(shù)據(jù),后面要訪問(wèn)這個(gè)數(shù)據(jù)庫(kù)就可以通過(guò)這個(gè)URI來(lái)指定,用于唯一標(biāo)識(shí)某個(gè)具體的ContentProvider
URI格式如下:
[content://][com.itchq.contentprovider][/artice][/1]
?|------A------|-----------------B-------------------|---C---|---D--|
A:前綴表明數(shù)據(jù)受控于一個(gè)內(nèi)容提供者。它從不修改,也就是schema,ContentProvider(內(nèi)容提供者)的scheme已經(jīng)由Android所規(guī)定, scheme為:content://
B:稱為Authority,它唯一地標(biāo)識(shí)了一個(gè)特定的Content Provider,因此,這部分內(nèi)容一般使用Content Provider所在的package來(lái)命名,使得它是唯一的,在上面的代碼中使用??? public static final String? AUTHORITY="com.itchq.contentprovider";定義這個(gè)變量,這個(gè)還有一個(gè)地方使用到,就是在AndroidMainfest.xml中要聲明這個(gè)數(shù)據(jù)庫(kù)的時(shí)候需要使用,如下代碼:
?
上面這個(gè)android:authorities這個(gè)就是和我們的AUTHORITY里面定義的字符串是一樣的,這個(gè)地方要注意點(diǎn),如果不一樣外部就不可以訪問(wèn)這個(gè)數(shù)據(jù)庫(kù)了,我們所說(shuō)的URI唯一性也是在這個(gè)地方來(lái)體現(xiàn)出來(lái)的,
C:稱為資源路徑,它表示所請(qǐng)求的資源的類型,這部分內(nèi)容是可選的
D:資源ID,它表示所請(qǐng)求的是一個(gè)特定的資源,它通常是一個(gè)數(shù)字,對(duì)應(yīng)前面我們所介紹的數(shù)據(jù)庫(kù)表中的_id字段的內(nèi)容,它唯一地標(biāo)志了某一種資源下的一個(gè)特定的實(shí)例
我們?cè)诙xURI的時(shí)候一般情況行都是需要定義這個(gè)C,但是D就沒有定義,簡(jiǎn)單點(diǎn)說(shuō)C就是代表了數(shù)據(jù)庫(kù)中某一個(gè)表(這個(gè)名字是隨意定義不一定就是和我們的數(shù)據(jù)庫(kù)表名字相同,這里只是一個(gè)比喻),而D則是表示操作這個(gè)表的哪一個(gè)字段,相當(dāng)于我們的SQL語(yǔ)句中 where id=1這個(gè)條件
繼續(xù)往下看:
/*MIME*/ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";這個(gè)是MIME類型,每一個(gè)ContenProvider中都必須定義這個(gè)MIME,其實(shí)這個(gè)MIME類型一般來(lái)說(shuō)就估計(jì)上面兩種,被分為兩部分前面是數(shù)據(jù)的大類別,后面定義具體的種類,比如上面的vnd.android.cursor.dir和vnd.android.cursor.item都是固定不會(huì)變的,后面的字段都是相同的而且隨便定義,一般的數(shù)據(jù)庫(kù)中都vnd.+包名。對(duì)于vnd.android.cursor.dir這個(gè)類型表示的訪問(wèn)多個(gè)資源的URI,就是你操作的是一整張表,比如添加新數(shù)據(jù)了,刪除某一個(gè)表后者是查詢表的全部記錄等等,而vnd.android.cursor.item是表示訪問(wèn)單個(gè)資源的URI,比如更新表中某一個(gè)自定的記錄,查詢某一條指定的信息,就是相當(dāng)SQL中的where條件語(yǔ)句。
?
Artice這個(gè)接口是我們這個(gè)數(shù)據(jù)庫(kù)的表的字段,這里看出這個(gè)表有三個(gè)字段,分別是:_id,title,contentid。ITEM和ITEM_POS這兩個(gè)變量分別定義URI匹配規(guī)則的匹配碼。
比如如果我們?cè)L問(wèn)的URI是content://com.itchq.contentprovider/articeitem,則匹配規(guī)則返回的匹配碼為ITEM。如果訪問(wèn)的是content://com.itchq.contentprovider/articeitem/# # 是一個(gè)通配符表示如何數(shù)據(jù),則匹配規(guī)則返回的匹配碼為ITEM_POS,這兩個(gè)常量我們后面還會(huì)有用到的
下面就是ContentProvider的定義了
?
package com.itchq.contentprovider;import java.util.HashMap;import com.itchq.contentprovider.ItemContentProvier.Artice;import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils;public class itContentProvider extends ContentProvider{private static final String DB_NAME="itchq.db";private static final String DB_TABLE_ARTICE="artice";private static final int DB_VERSION=1;private DBHelper dbHelper = null; private static final String DB_CREATE_ARTICE = "create table " + DB_TABLE_ARTICE + " (" + Artice.ID + " integer primary key autoincrement, " + Artice.TITLE + " text not null, " + Artice.CONTENT_ID + " integer not null);";private static final UriMatcher uriMatcher;private static final HashMap<String, String> articleProjectionMap;static{uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM, ItemContentProvier.ITEM);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);articleProjectionMap = new HashMap<String, String>(); articleProjectionMap.put(Artice.ID, Artice.ID); articleProjectionMap.put(Artice.TITLE, Artice.TITLE); articleProjectionMap.put(Artice.CONTENT_ID,Artice.CONTENT_ID); }@Overridepublic boolean onCreate() {// TODO Auto-generated method stubdbHelper = new DBHelper(getContext(), DB_NAME, null, DB_VERSION); return false;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getReadableDatabase(); SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder(); switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{sqlBuilder.setTables(DB_TABLE_ARTICE);sqlBuilder.setProjectionMap(articleProjectionMap); break;}case ItemContentProvier.ITEM_POS:{String id = uri.getPathSegments().get(1); sqlBuilder.setTables(DB_TABLE_ARTICE); sqlBuilder.setProjectionMap(articleProjectionMap); sqlBuilder.appendWhere(Artice.ID + "=" + id); break;}default: throw new IllegalArgumentException("Error Uri: " + uri); }Cursor cursor = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, TextUtils.isEmpty(sortOrder) ? ItemContentProvier.DEFAULT_SORT_ORDER : sortOrder);cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor;}@Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch (uriMatcher.match(uri)) { case ItemContentProvier.ITEM:{return ItemContentProvier.CONTENT_TYPE;}case ItemContentProvier.ITEM_POS:{return ItemContentProvier.CONTENT_ITEM_TYPE;}default:throw new IllegalArgumentException("Error Uri: " + uri); }}@Overridepublic Uri insert(Uri uri, ContentValues values) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getWritableDatabase();long id = db.insert(DB_TABLE_ARTICE, Artice.ID, values); if (id < 0) {throw new SQLiteException("Unable to insert " + values + " for "+ uri);}Uri newUri = ContentUris.withAppendedId(uri, id);getContext().getContentResolver().notifyChange(newUri,null);return newUri;}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0;switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{count = db.delete(DB_TABLE_ARTICE, selection, selectionArgs);break;}case ItemContentProvier.ITEM_POS:{String id = uri.getPathSegments().get(1);count = db.delete(DB_TABLE_ARTICE,Artice.ID + "=" + id + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs);break;}default:throw new IllegalArgumentException("Error Uri: " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {// TODO Auto-generated method stubSQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0;switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{count = db.update(DB_TABLE_ARTICE, values, selection, selectionArgs);break;}case ItemContentProvier.ITEM_POS:{String id = uri.getPathSegments().get(1);count = db.update(DB_TABLE_ARTICE, values, Artice.ID + "=" + id + (!TextUtils.isEmpty(selection) ? " and (" + selection + ')' : ""), selectionArgs);break;}default:throw new IllegalArgumentException("Error Uri: " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}private static class DBHelper extends SQLiteOpenHelper{public DBHelper(Context context, String name, CursorFactory factory,int version) {super(context, name, factory, version);// TODO Auto-generated constructor stub }@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stub db.execSQL(DB_CREATE_ARTICE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub }}}?
繼承ContentProvider類需要實(shí)現(xiàn)默認(rèn)下面這些方法
public boolean onCreate() 在創(chuàng)建ContentProvider時(shí)調(diào)用
?
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查詢指定Uri的ContentProvider,返回一個(gè)Cursor
?
public int delete(Uri uri, String selection, String[] selectionArgs) 用于從指定Uri的ContentProvider中刪除數(shù)據(jù)
?
public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) 用于更新指定Uri的ContentProvider中的數(shù)據(jù)
?
public Uri insert(Uri uri, ContentValues values) 用于添加新數(shù)據(jù)到指定Uri的ContentProvider中
?
public String getType(Uri uri) 用于返回指定的Uri中的數(shù)據(jù)的MIME類型
這個(gè)就是前面說(shuō)的,如果操作的數(shù)據(jù)屬于集合類型,那么MIME類型字符串應(yīng)該以vnd.android.cursor.dir/開頭。即
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider";?
如果要操作的數(shù)據(jù)屬于非集合類型數(shù)據(jù)(簡(jiǎn)單點(diǎn)說(shuō)就是指定where條件的),那么MIME類型字符串應(yīng)該以vnd.android.cursor.item/開頭。即
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";
?
UriMatcher類用于匹配Uri 這個(gè)就是要設(shè)置Uri的匹配規(guī)則,我們可以根據(jù)不同的Uri來(lái)操作不同的數(shù)據(jù),比如前面我們定義的那個(gè)URI
?
/*Content URI*/public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);?
分解這個(gè)URI字符串是”content://com.itchq.contentprovider/articeitem“,那么我們操作可以是content://com.itchq.contentprovider/articeitem這個(gè)字符串也也可能是content://com.itchq.contentprovider/articeitem/#這個(gè)字符串,前面表示操作整個(gè)數(shù)據(jù)庫(kù)表,后面指定是操作數(shù)據(jù)庫(kù)某一個(gè)字段,那我們是如何知道這個(gè)匹配碼呢?這個(gè)就需要UriMatcher類來(lái)設(shè)定
public void addURI (String authority, String path, int code) ;
authority就是上面定義的public static final String? AUTHORITY="com.itchq.contentprovider";這個(gè)變量,path就是就是指具體那個(gè)路徑,一般就是URI規(guī)則中表示C的部分,比如上面URI規(guī)則中?public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);那么這個(gè)path指的就是ARTICE_ITEM這個(gè)字符串變量,即public static final String ARTICE_ITEM="articeitem"; code就是后面那個(gè)匹配碼,比如我們可以看一下update更新語(yǔ)句里面有這么一個(gè)switch函數(shù):
?
switch(uriMatcher.match(uri)){case ItemContentProvier.ITEM:{}case ItemContentProvier.ITEM_POS:{}default:throw new IllegalArgumentException("Error Uri: " + uri);}?
上面的ItemContentProvier.ITEM 和 ItemContentProvier.ITEM_POS 就是已經(jīng)定義好的匹配碼,即code參數(shù),我們通過(guò)這個(gè)ItemContentProvier.ITEM可以知道操作整個(gè)數(shù)據(jù)庫(kù)表,ItemContentProvier.ITEM_POS操作的是數(shù)據(jù)庫(kù)表中某一列,這個(gè)匹配一般都是一開始初始化的時(shí)候就需要全部給注冊(cè)上的,
static{uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM, ItemContentProvier.ITEM);uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);這個(gè)就是注冊(cè)上的,我們先看這個(gè)path參數(shù)有ItemContentProvier.ARTICE_ITEM和ItemContentProvier.ARTICE_ITEM/#,這兩個(gè)就是分別表示操作的數(shù)據(jù)庫(kù)表和數(shù)據(jù)庫(kù)對(duì)應(yīng)某一列,最后一個(gè)參數(shù)就是我們上面也說(shuō)到的匹配碼,構(gòu)造函數(shù)的參數(shù)為UriMatcher.NO_MATCH,它表示當(dāng)uriMatcher不能匹配指定的URI時(shí),就返回代碼UriMatcher.NO_MATCH常量值為-1
public int match (Uri uri)
與傳入的Uri匹配,它會(huì)首先與找我們之前通過(guò)addURI方法添加進(jìn)來(lái)的Uri匹配,這個(gè)URI匹配是這樣的,前面部分固定是A部分:content://, B部分是authority,也就是我們addURI里面添加的第一個(gè)參數(shù),C部分表示是路徑,也就是我們?cè)赼ddRUI添加的第二個(gè)參數(shù)path,就是說(shuō)最后匹配的URI就是這樣的 content://authority/path。
authority和path都是我們?cè)赼ddURI參數(shù)就第一個(gè)和第二個(gè)的參數(shù)。如果匹配成功就返回之前我們?cè)O(shè)置的code值,否則返回一個(gè)UriMatcher.NO_MATCH常量值為-1
articleProjectionMap = new HashMap<String, String>(); articleProjectionMap.put(Artice.ID, Artice.ID); articleProjectionMap.put(Artice.TITLE, Artice.TITLE); articleProjectionMap.put(Artice.CONTENT_ID,Artice.CONTENT_ID);在上面的put函數(shù)中,第一個(gè)參數(shù)表示列的別名,第二個(gè)參數(shù)表示列的真實(shí)名稱。在這個(gè)例子中,我們把列的別名和和真實(shí)名稱都設(shè)置成一樣的。
在query函數(shù)中,我們使用SQLiteQueryBuilder來(lái)輔助數(shù)據(jù)庫(kù)查詢操作,使用這 個(gè)類的好處是我們可以不把數(shù)據(jù)庫(kù)表的字段暴露出來(lái),而是提供別名給第三方應(yīng)用程序使用,這樣就可以把數(shù)據(jù)庫(kù)表內(nèi)部設(shè)計(jì)隱藏起來(lái),方便后續(xù)擴(kuò)展和維護(hù)。列別 名到真實(shí)列名的映射是由上面這個(gè)HashMap成員變量來(lái)實(shí)現(xiàn)的,當(dāng)然這里沒有修改,也可以不用定義。
@Overridepublic String getType(Uri uri) {// TODO Auto-generated method stubswitch (uriMatcher.match(uri)) { case ItemContentProvier.ITEM:{return ItemContentProvier.CONTENT_TYPE;}case ItemContentProvier.ITEM_POS:{return ItemContentProvier.CONTENT_ITEM_TYPE;}default:throw new IllegalArgumentException("Error Uri: " + uri); }}這個(gè)是返回當(dāng)前Uri所數(shù)據(jù)的MIME類型,如果該Uri對(duì)應(yīng)的數(shù)據(jù)可能包括多條記錄,那么MIME類型字符串就是以 vnd.android.cursor.dir/開頭,我們通過(guò)前面的addURI指定 ItemContentProvier.ITEM是返回全面的類型,如果Uri對(duì)應(yīng)的數(shù)據(jù)只包含一條記錄,那么MIME類型字符串就是以 vnd.android.cursor.item/開頭,前面的addURI得知ItemContentProvier.ITEM_POS這個(gè)返回的是某一列信息。
還有一點(diǎn)是我們?cè)趇tContentProvider類的內(nèi)部中定義了一個(gè)DBHelper類,它繼承于SQLiteOpenHelper類,它用是用輔助我 們操作數(shù)據(jù)庫(kù)的。使用這個(gè)DBHelper類來(lái)輔助操作數(shù)據(jù)庫(kù)的好處是只有當(dāng)我們第一次對(duì)數(shù)據(jù)庫(kù)時(shí)行操作時(shí),系統(tǒng)才會(huì)執(zhí)行打開數(shù)據(jù)庫(kù)文件的操作,
剩下的更新查詢刪除等操作就不多說(shuō)了,最后不要忘記在AndroidManifest.xml中進(jìn)行定義
<providerandroid:name="com.itchq.contentprovider.itContentProvider"android:authorities="com.itchq.contentprovider"android:multiprocess="false"/>在配置Content Provider的時(shí)候,最重要的就是要指定它的authorities屬性了,只有配置了這個(gè)屬性,第三方應(yīng)用程序才能通過(guò)它來(lái)找到這個(gè)Content Provider。這要需要注意的,這里配置的authorities屬性的值是和我們前面在ItemContentProvier.java文件中定義的AUTHORITY 常量的值是一致的。另外一個(gè)屬性multiprocess是一個(gè)布爾值,它表示這個(gè)Content Provider是否可以在每個(gè)客戶進(jìn)程中創(chuàng)建一個(gè)實(shí)例,這樣做的目的是為了減少進(jìn)程間通信的開銷。這里我們?yōu)榱藴p少不必要的內(nèi)存開銷,把屬性 multiprocess的值設(shè)置為false,使得系統(tǒng)只能有一個(gè)Content Provider實(shí)例存在,它運(yùn)行在自己的進(jìn)程中。下面看看在Activity中投入和操作這些數(shù)據(jù)庫(kù)的
private void add(){ContentValues values = new ContentValues();values.put(Artice.TITLE, "test");values.put(Artice.CONTENT_ID, 1);Uri uri = getContentResolver().insert(ItemContentProvier.CONTENT_ARTICE, values);}private void query(){String[] projection = new String[] { Artice.ID, Artice.TITLE, Artice.CONTENT_ID};Cursor cursor = getContentResolver().query(ItemContentProvier.CONTENT_ARTICE, projection, null, null, ItemContentProvier.DEFAULT_SORT_ORDER);if (cursor.moveToFirst()) {do{int id=cursor.getInt(cursor.getColumnIndex(Artice.ID));String title=cursor.getString(cursor.getColumnIndex(Artice.TITLE));int content_id=cursor.getInt(cursor.getColumnIndex(Artice.CONTENT_ID));}while(cursor.moveToNext());}} ItemContentProvier.CONTENT_ARTICE就是我們前面定義的那個(gè)URI這個(gè)介紹一個(gè)ContentUris類,這個(gè)類是它用于在Uri后面追加一個(gè)ID或者解析出傳入的Uri所帶上的ID值,就是我們說(shuō)的URI規(guī)則中最后D部分
|------A------|-----------------B-------------------|---C---|---D--|
常用的兩個(gè)方法如下:
public static Uri withAppendedId (Uri contentUri, long id)??用于為路徑加上ID部分
Uri resultUri = ContentUris.withAppendedId(ItemContentProvier.CONTENT_ARTICE, 10);
那么這個(gè)Uri就是content://com.itchq.contentprovider/articeitem/10,那么我們?cè)谇懊媸褂肬riMatch.addURI匹配碼就是
uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);
這個(gè),那么這個(gè)時(shí)候返回的匹配碼就是 ItemContentProvier.ITEM_POS。
public static long parseId (Uri contentUri)從路徑中獲取ID部分
Uri uri = Uri.parse("content://com.itchq.contentprovider/articeitem/10"
long personid = ContentUris.parseId(uri);//獲取的結(jié)果為:10,前面的操作中有用到這個(gè)
轉(zhuǎn)載于:https://www.cnblogs.com/itchq/p/4014388.html
總結(jié)
以上是生活随笔為你收集整理的Android ContentProvider介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: RDD:基于内存的集群计算容错抽象
- 下一篇: 加解压开源组件-SharpZipLib