除了共享內(nèi)存(SDCard)的數(shù)據(jù)外,其他包括SQLite、SharedPreferences都是僅限于被當(dāng)前所創(chuàng)建的應(yīng)用訪問,而無法使它們的數(shù)據(jù)在應(yīng)用程序之間交換數(shù)據(jù),所以Android提供了ContentProvider,ContentProvider具有以下特點:
- 應(yīng)用程序間共享數(shù)據(jù)的一種方式
- 為存儲和獲取數(shù)據(jù)提供統(tǒng)一接口
- android為一些常見的數(shù)據(jù)提供了ContentProvider:
- Browser:存儲如瀏覽器的信息。
- CallLog:存儲通話記錄等信息。
- Contacts:存儲聯(lián)系人等信息。
- MediaStore:存儲媒體文件的信息。
- Settings:存儲設(shè)備的設(shè)置和首選項信息。

訪問ContentProvider
ContentProvider 實例通過處理來自其他應(yīng)用的請求來管理對結(jié)構(gòu)化數(shù)據(jù)集的訪問。所有形式的訪問最終都會調(diào)用 ContentResolver,后者接著調(diào)用ContentProvider的具體方法來獲取訪問權(quán)限。
一般使用Context.getContentResolver()方法獲取ContentResolver對象。
ContentResolver 可提供insert、query、update、delete等方法.
例如,要從用戶字典提供程序中獲取字詞及其區(qū)域設(shè)置的列表,則需調(diào)用 ContentResolver.query()。
// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI,
mProjection,
mSelectionClause
mSelectionArgs,
mSortOrder);
| query() 參數(shù) | SELECT 關(guān)鍵字/參數(shù) | 備注 |
|---|---|---|
| Uri | FROM table_name | Uri 映射至名為 table_name 的提供程序中的表。 |
| projection | col,col,col,... | projection 是應(yīng)該為檢索到的每個行包含的列的數(shù)組。 |
| selection | WHERE col = value | selection 會指定選擇行的條件。 |
| selectionArgs | (沒有完全等效項。選擇參數(shù)會替換選擇子句中 ? 的占位符。) | |
| sortOrder | ORDER BY col,col,... | sortOrder 指定行在返回的 Cursor 中的顯示順序。 |
內(nèi)容 URI
在Android中,Uri是一種比較常見的資源訪問方式。每一個ContentProvider都擁有一個公共的URI,這個URI用于表示這個ContentProvider所提供的數(shù)據(jù)。
<srandard_prefix>://<authority>/<data_path>/<id>
- <srandard_prefix>:
ContentProvider的標(biāo)準(zhǔn)前綴始終是content://。 - <authority>:URI 的標(biāo)識,用于唯一標(biāo)識這個ContentProvider,外部調(diào)用者可以根據(jù)這個標(biāo)識來找到它。它定義了是哪個Content Provider提供這些數(shù)據(jù)。對于第三方應(yīng)用程序,為了保證URI標(biāo)識的唯一性,它必須是一個完整的、小寫的類名。一般為該ContentProvider的包.類的名稱
- <data_path>:請求的數(shù)據(jù)類型, 如數(shù)據(jù)庫的表。
- <id>:指定請求的特定數(shù)據(jù)。如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應(yīng)的數(shù)據(jù),如果沒有ID,就表示返回全部
content://user_dictionary/words
content://com.example.app.provider/table3/1
//對應(yīng)由 表table3中1標(biāo)識的行的內(nèi)容 URI。
content://com.example.app.provider/table3/1/name
//對應(yīng)由 表table3中1標(biāo)識的行的name字段內(nèi)容 URI。
UriMatcher
UriMatcher會將內(nèi)容 URI“模式”映射到整型值。 可以在一個 switch 語句中使用這些整型值,為匹配特定模式的一個或多個內(nèi)容 URI 執(zhí)行相應(yīng)操作。
內(nèi)容 URI 模式使用通配符匹配內(nèi)容 URI:
*:匹配由任意長度的任何有效字符組成的字符串
#:匹配由任意長度的數(shù)字字符組成的字符串
假設(shè)一個具有權(quán)限 com.example.app.provider的提供程序能識別以下指向表的內(nèi)容 URI:
content://com.example.app.provider/table1:一個名為 table1 的表
content://com.example.app.provider/table2/dataset1:一個名為 dataset1 的表
content://com.example.app.provider/table2/dataset2:一個名為 dataset2 的表
content://com.example.app.provider/table3:一個名為 table3 的表
可以使用以下內(nèi)容 URI 模式:
content://com.example.app.provider/*
//匹配提供程序中的任何內(nèi)容 URI。
content://com.example.app.provider/table2/*:
//匹配表 dataset1 和表 dataset2 的內(nèi)容 URI,但不匹配 table1 或 table3 的內(nèi)容 URI。
content://com.example.app.provider/table3/#
//匹配 table3 中單個行的內(nèi)容 URI,
//如 content://com.example.app.provider/table3/6 對應(yīng)由 6 標(biāo)識的行的內(nèi)容 URI。
方法 addURI() 會將權(quán)限和路徑映射到一個整型值。 方法 match() 會返回 URI 的整型值。switch 語句會在查詢整個表與查詢單個記錄之間進行選擇:
public class ExampleProvider extends ContentProvider {
...
// Creates a UriMatcher object.
private static final UriMatcher sUriMatcher;
sUriMatcher.addURI("com.example.app.provider", "table3", 1);
sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
...
// Implements ContentProvider.query()
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
...
switch (sUriMatcher.match(uri)) {
// If the incoming URI was for all of table3
case 1:
if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
break;
// If the incoming URI was for a single row
case 2:
/*
Because this URI was for a single row, the _ID value part is
present. Get the last path segment from the URI;
this is the _ID value.Then, append the value to the WHERE
clause for the query
*/
selection = selection + "_ID = " uri.getLastPathSegment();
break;
default:
...
// If the URI is not recognized, you should do some error handling here.
}
// call the code to actually do the query
}
創(chuàng)建ContentProvider
在Android中,如果要創(chuàng)建自己的內(nèi)容提供者的時候,需要擴展抽象類ContentProvider,并重寫其中定義的各種方法。然后在AndroidManifest.xml文件中注冊該ContentProvider。
創(chuàng)建ContentProvider的步驟:
- 創(chuàng)建一個ContentProvider的子類。
- 定義內(nèi)容Uri
- 創(chuàng)建SQLiteOpenHelper的子類,創(chuàng)建一個用于存儲內(nèi)容的數(shù)據(jù)庫。
- 在ContentProvider的子類中實現(xiàn)query()、insert()、update()、delete()、getType()、onCreate()方法。
- 在AndroidManifest.xml文件中注冊自定義的ContentProvider。
<provider.../>:一般只需要設(shè)置兩個屬性即可訪問,一些額外的屬性就是為了設(shè)置訪問權(quán)限而存在的:
android:name:provider的響應(yīng)類。
android:authorities:Provider的唯一標(biāo)識,用于Uri匹配,一般為ContentProvider類的全名。

在實現(xiàn)ContentProvider的方法時需要注意一下幾點:
- 所有這些方法(onCreate() 除外)都可由多個線程同時調(diào)用,因此它們必須是線程安全方法。
- 避免在 onCreate() 中執(zhí)行長時間操作。將初始化任務(wù)推遲到實際需要時進行。
- 盡管必須實現(xiàn)這些方法,但代碼只需返回要求的數(shù)據(jù)類型,無需執(zhí)行任何其他操作。 例如,可能想防止其他應(yīng)用向某些表插入數(shù)據(jù)。 要實現(xiàn)此目的,可以忽略 insert() 調(diào)用并返回 0。