除了共享內(nèi)存(SDCard)的數(shù)據(jù)外砚殿,其他包括SQLite似炎、SharedPreferences都是僅限于被當(dāng)前所創(chuàng)建的應(yīng)用訪問,而無法使它們的數(shù)據(jù)在應(yīng)用程序之間交換數(shù)據(jù)叹阔,所以Android提供了ContentProvider传睹,ContentProvider具有以下特點(diǎn):
- 應(yīng)用程序間共享數(shù)據(jù)的一種方式
- 為存儲(chǔ)和獲取數(shù)據(jù)提供統(tǒng)一接口
- android為一些常見的數(shù)據(jù)提供了ContentProvider:
- Browser:存儲(chǔ)如瀏覽器的信息欧啤。
- CallLog:存儲(chǔ)通話記錄等信息。
- Contacts:存儲(chǔ)聯(lián)系人等信息店印。
- MediaStore:存儲(chǔ)媒體文件的信息倒慧。
- Settings:存儲(chǔ)設(shè)備的設(shè)置和首選項(xiàng)信息。
訪問ContentProvider
ContentProvider 實(shí)例通過處理來自其他應(yīng)用的請(qǐng)求來管理對(duì)結(jié)構(gòu)化數(shù)據(jù)集的訪問掠河。所有形式的訪問最終都會(huì)調(diào)用 ContentResolver唠摹,后者接著調(diào)用ContentProvider的具體方法來獲取訪問權(quán)限奉瘤。
一般使用Context.getContentResolver()方法獲取ContentResolver對(duì)象。
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)該為檢索到的每個(gè)行包含的列的數(shù)組。 |
selection | WHERE col = value | selection 會(huì)指定選擇行的條件是趴。 |
selectionArgs | (沒有完全等效項(xiàng)澄惊。選擇參數(shù)會(huì)替換選擇子句中 ? 的占位符掸驱。) | |
sortOrder | ORDER BY col,col,... | sortOrder 指定行在返回的 Cursor 中的顯示順序。 |
內(nèi)容 URI
在Android中滚婉,Uri是一種比較常見的資源訪問方式帅刀。每一個(gè)ContentProvider都擁有一個(gè)公共的URI,這個(gè)URI用于表示這個(gè)ContentProvider所提供的數(shù)據(jù)骇窍。
<srandard_prefix>://<authority>/<data_path>/<id>
- <srandard_prefix>:
ContentProvider的標(biāo)準(zhǔn)前綴始終是content://腹纳。 - <authority>:URI 的標(biāo)識(shí)驱犹,用于唯一標(biāo)識(shí)這個(gè)ContentProvider,外部調(diào)用者可以根據(jù)這個(gè)標(biāo)識(shí)來找到它佃牛。它定義了是哪個(gè)Content Provider提供這些數(shù)據(jù)医舆。對(duì)于第三方應(yīng)用程序,為了保證URI標(biāo)識(shí)的唯一性爷速,它必須是一個(gè)完整的惫东、小寫的類名。一般為該ContentProvider的包.類的名稱
- <data_path>:請(qǐng)求的數(shù)據(jù)類型, 如數(shù)據(jù)庫的表禁谦。
- <id>:指定請(qǐng)求的特定數(shù)據(jù)废封。如果URI中包含表示需要獲取的記錄的ID漂洋;則就返回該id對(duì)應(yīng)的數(shù)據(jù),如果沒有ID刽漂,就表示返回全部
content://user_dictionary/words
content://com.example.app.provider/table3/1
//對(duì)應(yīng)由 表table3中1標(biāo)識(shí)的行的內(nèi)容 URI贝咙。
content://com.example.app.provider/table3/1/name
//對(duì)應(yīng)由 表table3中1標(biāo)識(shí)的行的name字段內(nèi)容 URI。
UriMatcher
UriMatcher會(huì)將內(nèi)容 URI“模式”映射到整型值窟她。 可以在一個(gè) switch 語句中使用這些整型值震糖,為匹配特定模式的一個(gè)或多個(gè)內(nèi)容 URI 執(zhí)行相應(yīng)操作趴腋。
內(nèi)容 URI 模式使用通配符匹配內(nèi)容 URI:
*:匹配由任意長度的任何有效字符組成的字符串
#:匹配由任意長度的數(shù)字字符組成的字符串
假設(shè)一個(gè)具有權(quán)限 com.example.app.provider的提供程序能識(shí)別以下指向表的內(nèi)容 URI:
content://com.example.app.provider/table1:一個(gè)名為 table1 的表
content://com.example.app.provider/table2/dataset1:一個(gè)名為 dataset1 的表
content://com.example.app.provider/table2/dataset2:一個(gè)名為 dataset2 的表
content://com.example.app.provider/table3:一個(gè)名為 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 中單個(gè)行的內(nèi)容 URI糊余,
//如 content://com.example.app.provider/table3/6 對(duì)應(yīng)由 6 標(biāo)識(shí)的行的內(nèi)容 URI贬芥。
方法 addURI() 會(huì)將權(quán)限和路徑映射到一個(gè)整型值。 方法 match() 會(huì)返回 URI 的整型值昏苏。switch 語句會(huì)在查詢整個(gè)表與查詢單個(gè)記錄之間進(jìn)行選擇:
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中贤惯,如果要?jiǎng)?chuàng)建自己的內(nèi)容提供者的時(shí)候棒掠,需要擴(kuò)展抽象類ContentProvider,并重寫其中定義的各種方法颈墅。然后在AndroidManifest.xml文件中注冊(cè)該ContentProvider雾袱。
創(chuàng)建ContentProvider的步驟:
- 創(chuàng)建一個(gè)ContentProvider的子類芹橡。
- 定義內(nèi)容Uri
- 創(chuàng)建SQLiteOpenHelper的子類,創(chuàng)建一個(gè)用于存儲(chǔ)內(nèi)容的數(shù)據(jù)庫煎殷。
- 在ContentProvider的子類中實(shí)現(xiàn)query()腿箩、insert()度秘、update()、delete()唆貌、getType()垢乙、onCreate()方法钩骇。
- 在AndroidManifest.xml文件中注冊(cè)自定義的ContentProvider册烈。
<provider.../>:一般只需要設(shè)置兩個(gè)屬性即可訪問骂倘,一些額外的屬性就是為了設(shè)置訪問權(quán)限而存在的:
android:name:provider的響應(yīng)類历涝。
android:authorities:Provider的唯一標(biāo)識(shí),用于Uri匹配堰塌,一般為ContentProvider類的全名分衫。
在實(shí)現(xiàn)ContentProvider的方法時(shí)需要注意一下幾點(diǎn):
- 所有這些方法(onCreate() 除外)都可由多個(gè)線程同時(shí)調(diào)用蚪战,因此它們必須是線程安全方法。
- 避免在 onCreate() 中執(zhí)行長時(shí)間操作施籍。將初始化任務(wù)推遲到實(shí)際需要時(shí)進(jìn)行丑慎。
- 盡管必須實(shí)現(xiàn)這些方法瓤摧,但代碼只需返回要求的數(shù)據(jù)類型,無需執(zhí)行任何其他操作腻异。 例如这揣,可能想防止其他應(yīng)用向某些表插入數(shù)據(jù)。 要實(shí)現(xiàn)此目的给赞,可以忽略 insert() 調(diào)用并返回 0片迅。