Android四大組件之Activity
Android四大組件之Service
Android四大組件之BroadcastReceiver
Android四大組件之ContentProvider
一、內(nèi)容提供器簡介
內(nèi)容提供器(ContentProvider)主要用于在不同的應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)共享的功能,它提供了一套完整的機(jī)制,允許一個(gè)程序訪問另一個(gè)程序中的數(shù)據(jù),同時(shí)還能保證被訪問數(shù)據(jù)的安全性。目前,使用內(nèi)容提供器是Android實(shí)現(xiàn)跨進(jìn)程共享數(shù)據(jù)的標(biāo)準(zhǔn)方式。
不同于文件存儲和SharedPreferences存儲中的兩種全局可讀寫操作模式,內(nèi)容提供者可以選擇只對哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,從而保證我們程序中的隱私數(shù)據(jù)不會有泄露的風(fēng)險(xiǎn)。
二、訪問其他程序中的數(shù)據(jù)
內(nèi)容提供器的用法一般有兩種,一種是使用現(xiàn)有的內(nèi)容提供器來讀取和操作相應(yīng)程序中的數(shù)據(jù),另一種是創(chuàng)建自己的內(nèi)容提供器給我們程序的數(shù)據(jù)提供外部訪問接口。如果一個(gè)應(yīng)用程序通過內(nèi)容提供器對其數(shù)據(jù)提供了外部訪問接口,那么任何其他的應(yīng)用程序就都可以對這部分?jǐn)?shù)據(jù)進(jìn)行訪問。Android系統(tǒng)中自帶的電話簿、短信。多媒體等程序都提供了類似的訪問接口,這就使得第三方應(yīng)用程序可以充分地利用使用這些數(shù)據(jù)。
2.1 ContentResolver的基本用法
對于每一個(gè)應(yīng)用程序來說,如果想要訪問費(fèi)用提供器中共享的數(shù)據(jù),就一定要借助ContentResolver類,可以通過Context中的getContentResolver()方法獲取到該類的實(shí)例。
ContentResolver中提供了一系列的方法用于對數(shù)據(jù)進(jìn)行CRUD操作,其中insert()方法用于添加數(shù)據(jù),update()方法用于更新數(shù)據(jù),delete()方法用于刪除數(shù)據(jù),query()方法用于查詢數(shù)據(jù)。這種方式和SQLiteDatabase中方法基本類似,只不過在方法參數(shù)上稍微有些區(qū)別。
不同于SQLiteDatabase,ContentResolver中的增刪改查方法都是不接收表名參數(shù)的,而是使用一個(gè)Uri參數(shù)代替,這個(gè)參數(shù)被程蓉內(nèi)容URI。內(nèi)容URI給內(nèi)容提供器中的數(shù)據(jù)建立了唯一表示符,主要由兩部分組成:authority和path。authority是用于對不同的應(yīng)用程序進(jìn)行區(qū)分的,一般為了避免沖突,都會采用程序包名的方式進(jìn)行命名。例如com.monkey.contentprovider。path則是用于對同一引用程序中的不同表名做區(qū)分的,通常天驕到authority的后面。將authority和path進(jìn)行組合,再加上頭布局的協(xié)議聲明,就可以完成標(biāo)準(zhǔn)的URI格式寫法。如:
content://com.monkey.contentprovider/table1
content://com.monkey.contentprovider/table2
由此可以看出,內(nèi)容URI可以非常清楚的表達(dá)出我們想要訪問哪個(gè)程序中哪張表的數(shù)據(jù)。也正以為如此,ContentResolver中的增刪改查方法才都接收Uri對象作為參數(shù),因?yàn)槿绻褂帽砻脑?,系統(tǒng)將無法得知我們期望訪問的是哪個(gè)應(yīng)用程序里的表。
在得到內(nèi)容URI字符串后,我們還需要將它解析成Uri對象才可以最為參數(shù)穿日。具體解析方法如下:
Uri uri = Uri.parse("content://com.monkey.contentprovider/table1");
下來我們就可以使用這個(gè)Uri對象來查詢table1表中的數(shù)據(jù)了,代碼如下:
Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
這些參數(shù)和sql中query()方法的參數(shù)很像,我們通過比較來學(xué)習(xí)下:
| query()方法參數(shù) | 對應(yīng)sql部分 | 描述 |
|---|---|---|
| uri | from table_name | 指定查詢某個(gè)應(yīng)用程序下的某張表 |
| projection | select column1,column2 | 指定禪熏的列名 |
| selection | where column = value | 指定where的約束條件 |
| selectionArgs | - | 為where中的占位符提供具體的值 |
| sortOrder | order by column1 | 指定查詢結(jié)果的排序方式 |
2.1.1 查詢
查詢完成后返回的仍然是一個(gè)Cursor對象,這是我們可以將數(shù)據(jù)沖Cursor對象中逐個(gè)讀取出來。通過移動游標(biāo)的位置來遍歷Cursor的所有上,然后再取出每一行中相應(yīng)列的數(shù)據(jù)
if (null != cursor){
while (cursor.moveToNext()){
String colunm1 = cursor.getString(cursor.getColumnIndex("colunm1"));
int colunm2 = cursor.getInt(cursor.getColumnIndex("colunm1"));
}
//使用完關(guān)閉
cursor.close();
}
2.1.2 增加
向table1表中添加一條數(shù)據(jù),具體實(shí)現(xiàn)如下:
ContentValues contentValues = new ContentValues();
contentValues.put("colunm1","test");
contentValues.put("colunm2",1);
getContentResolver().insert(uri,contentValues);
2.1.3 修改
由此看出,是將待添加的數(shù)據(jù)組裝到ContentValues中,然后調(diào)用ContentResolver的insert()方法,將uri和ContentValues作為參數(shù)傳入。
更新數(shù)據(jù),修改column1的值,可以使用ContentResolver的update()方法實(shí)現(xiàn):
ContentValues contentValues = new ContentValues();
contentValues.put("colunm1","測試");
getContentResolver().update(uri,contentValues,"column1 = test",new String[]{"test","1"});
2.1.4 刪除
刪除數(shù)據(jù),調(diào)用ContentResolver的delete()方法刪除。具體代碼如下:
getContentResolver().delete(uri,"column1 = '測試'",new String[]{"測試","1"});
以上就是ContentResolver中的增刪改查,需要注意的是uri這個(gè)參數(shù)。
三、創(chuàng)建自己的內(nèi)容提供器
在上面,我們學(xué)習(xí)了如何在自己的程序中訪問其他應(yīng)用程序的數(shù)據(jù)。我們只需要獲取到該應(yīng)用程序的內(nèi)容uri,然后借助
ContentResolver進(jìn)行CRUD操作就可以實(shí)現(xiàn)。下面我們學(xué)習(xí)下怎樣創(chuàng)建自己的內(nèi)容提供器。
3.1 創(chuàng)建內(nèi)容提供器
要想實(shí)現(xiàn)跨程序共享數(shù)據(jù)的功能,官方推薦使用內(nèi)容提供器嗎,可以通過繼承ContentProvider的方式實(shí)現(xiàn)。ContentProvider類中有6個(gè)抽象方法,我們在使用子類繼承的時(shí)候,需要重寫這6個(gè)方法,代碼如下:
public class MyContentProvider extends ContentProvider {
public MyContentProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
}
下面了解下這6個(gè)方法:
1.onCreate()
初始化內(nèi)容提供者的時(shí)候調(diào)用。通常會在這里完成對數(shù)據(jù)庫的創(chuàng)建和升級操作。返回true,表示內(nèi)容提供器初始化成功,false則失敗。只有當(dāng)存在ContentResolver訪問我們程序數(shù)據(jù)時(shí),它才會被初始化。
2.query()
查詢數(shù)據(jù)。使用uri參數(shù)來確定查詢那張表,projection參數(shù)用于確定查詢那些列,selection和selectionArgs參數(shù)用戶約束查詢那些行,sortOrder參數(shù)用于對結(jié)果進(jìn)行排序,查詢結(jié)果存放在Curosr對象中返回。
3.insert()
添加數(shù)據(jù)。使用uri參數(shù)來確定要添加到的表,待添加的數(shù)據(jù)保存在values參數(shù)中。添加完成后,返回一個(gè)用于表示這條新紀(jì)錄的URI。
4.update()
修改已有數(shù)據(jù)。使用uri參數(shù)來確定更新那一張表中的數(shù)據(jù),新數(shù)據(jù)保存在values參數(shù)中,selection和selectionArgs參數(shù)用于約束那些行,將受影響的行數(shù)作為返回值返回。
5.delete()
刪除數(shù)據(jù)。使用uri參數(shù)來確定刪除哪一張表中的數(shù)據(jù),selection和selectionArgs參數(shù)用戶約束刪除那些行,被刪除的行數(shù)將作為參數(shù)值返回。
6.getType()
根據(jù)傳入的內(nèi)容uri來返回相應(yīng)的MIME類型(在下文中會介紹什么是MIME類型)。
3.2 UriMatcher類
在ContentProvider 中注冊uri ,根據(jù) uri 匹配 ContentProvider 中對應(yīng)的數(shù)據(jù)表。具體代碼如下:
public static final int URI_CODE_TALBE1 = 1;
public static final int URI_CODE_TALBE2 = 2;
private static UriMatcher uriMatcher;
static {
//創(chuàng)建uriMaticher對象
//常量UriMatcher.NO_MATCH = 不匹配任何路徑的返回碼
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//在ContentProvider 中注冊URI(addURI())
// 若URI資源路徑 = content://com.monkey.contentprovider/table1 ,則返回注冊碼URI_CODE_TALBE1
uriMatcher.addURI("com.monkey.contentprovider", "table1", URI_CODE_TALBE1);
// 若URI資源路徑 = content://com.monkey.contentprovider/table2 ,則返回注冊碼URI_CODE_TALBE2
uriMatcher.addURI("com.monkey.contentprovider", "table2", URI_CODE_TALBE2);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case URI_CODE_TALBE1:
// cursor = 查詢table1中的數(shù)據(jù)
break;
case URI_CODE_TALBE2:
// cursor = 查詢table2中的數(shù)據(jù)
break;
}
return cursor;
}
我們在上面創(chuàng)建了UriMatcher實(shí)例,通過addaddURI()方法,將我們期匹配的內(nèi)容uri格式傳進(jìn)去,當(dāng)query()方法被調(diào)用的時(shí)候,將會通過UriMatcher的match()方法匹配傳進(jìn)來的uri,我們就可以知道調(diào)用方需要訪問什么數(shù)據(jù)了。對于insert()、update()、delete()方法的實(shí)現(xiàn)是類似的。
3.3 MIME數(shù)據(jù)類型
指定某個(gè)擴(kuò)展名的文件用某種應(yīng)用程序來打開
MIME類型組成
每種MIME類型 由2部分組成 = 類型 + 子類型
// 類型 = text、子類型 = html
text/html //超文本標(biāo)記語言文本
// 類型 = application、子類型 = vnd.android.package-archive
application/vnd.android.package-archive /APK文件(安卓系統(tǒng))
在內(nèi)容提供器中g(shù)etType()方法用于獲取Uri對象所對應(yīng)的MIME類型
android做了如下格式規(guī)定:
- 如果內(nèi)容URI以路徑結(jié)尾,則后接android.cursor.dir/ ,如果內(nèi)容URI以id結(jié)尾,則后接android.cursor.item/。
- 后面接上vnd.<authority>.<path>,必須以vnd開頭。
對于content://com.monkey.contentprovider/table1這個(gè)內(nèi)容URI,所對應(yīng)的MIME類型為android.cursor.dir/vnd.com.monkey.contentprovider/table1
對于content://com.monkey.contentprovider/table1/5這個(gè)內(nèi)容URI,所對應(yīng)的MIME類型為android.cursor.item/vnd.com.monkey.contentprovider/table1
在getType()中的實(shí)現(xiàn)如下:
public static final int URI_CODE_TALBE1 = 1;
public static final int URI_CODE_TALBE2 = 2;
public static final int URI_CODE_TALBE1_ITEM = 3;
private static UriMatcher uriMatcher;
static {
//創(chuàng)建uriMaticher對象
//常量UriMatcher.NO_MATCH = 不匹配任何路徑的返回碼
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//在ContentProvider 中注冊URI(addURI())
// 若URI資源路徑 = content://com.monkey.contentprovider/table1 ,則返回注冊碼URI_CODE_TALBE1
uriMatcher.addURI("com.monkey.contentprovider", "table1", URI_CODE_TALBE1);
// 若URI資源路徑 = content://com.monkey.contentprovider/table2 ,則返回注冊碼URI_CODE_TALBE2
uriMatcher.addURI("com.monkey.contentprovider", "table2", URI_CODE_TALBE2);
// 若URI資源路徑 = content://com.monkey.contentprovider/table1/5 ,則返回注冊碼URI_CODE_TALBE1_ITEM
uriMatcher.addURI("com.monkey.contentprovider", "table1/#", URI_CODE_TALBE1_ITEM);
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case URI_CODE_TALBE1:
return "android.cursor.dir/vnd.com.monkey.contentprovider/table1";
case URI_CODE_TALBE2:
return "android.cursor.dir/vnd.com.monkey.contentprovider/table2";
case URI_CODE_TALBE1_ITEM:
return "android.cursor.item/vnd.com.monkey.contentprovider/table2";
default:
break;
}
return null;
}