ContentProvider是Android應(yīng)用程序的主要構(gòu)建模塊之一萌狂,可為應(yīng)用程序提供內(nèi)容。它們封裝數(shù)據(jù)并通過單個ContentResolver接口將其提供給應(yīng)用程序 褪测。僅當(dāng)您需要在多個應(yīng)用程序之間共享數(shù)據(jù)時,才需要內(nèi)容提供程序啤挎。例如岂却,聯(lián)系人數(shù)據(jù)由多個應(yīng)用程序使用空镜,并且必須存儲在內(nèi)容提供商中。如果您不需要在多個應(yīng)用程序之間共享數(shù)據(jù)最冰,則可以直接通過使用數(shù)據(jù)庫 SQLiteDatabase典鸡。
當(dāng)通過發(fā)出請求時ContentResolver罚攀,系統(tǒng)將檢查給定URI的權(quán)限潮针,并將請求傳遞給在該權(quán)限下注冊的內(nèi)容提供者术荤。內(nèi)容提供商可以根據(jù)需要解釋其余的URI。的UriMatcher班是解析URI的幫助每篷。
需要實現(xiàn)的主要方法是:
- onCreate() 用來初始化提供者
- query(Uri, String[], Bundle, CancellationSignal) 它將數(shù)據(jù)返回給調(diào)用者
- insert(Uri, ContentValues) 將新數(shù)據(jù)插入內(nèi)容提供者
- update(Uri, ContentValues, Bundle) 更新內(nèi)容提供者中的現(xiàn)有數(shù)據(jù)
- delete(Uri, Bundle) 從內(nèi)容提供商刪除數(shù)據(jù)
- getType(Uri) 返回內(nèi)容提供者中數(shù)據(jù)的MIME類型
數(shù)據(jù)訪問方法(例如insert(Uri, ContentValues)和 update(Uri, ContentValues, Bundle))可以同時從多個線程中調(diào)用瓣戚,并且必須是線程安全的。其他方法(例如onCreate())只能從應(yīng)用程序主線程中調(diào)用焦读,并且必須避免執(zhí)行冗長的操作子库。有關(guān)其預(yù)期的線程行為,請參見方法說明矗晃。
請求ContentResolver會自動轉(zhuǎn)發(fā)到適當(dāng)?shù)腃ontentProvider實例刚照,因此子類不必擔(dān)心跨進程調(diào)用的細節(jié)。
開發(fā)人員指南
有關(guān)使用內(nèi)容提供程序的更多信息喧兄,請閱讀 內(nèi)容提供程序 開發(fā)人員指南。
開發(fā)人員指南部分
內(nèi)容提供商基礎(chǔ)
內(nèi)容提供商管理對中央數(shù)據(jù)存儲庫的訪問啊楚。提供程序是Android應(yīng)用程序的一部分吠冤,該應(yīng)用程序通常提供自己的UI來處理數(shù)據(jù)。但是恭理,內(nèi)容提供程序主要旨在供其他應(yīng)用程序使用拯辙,其他應(yīng)用程序使用提供程序客戶端對象訪問提供程序。提供者和提供者客戶端共同為數(shù)據(jù)提供一致的標準接口,該接口還處理進程間通信和安全的數(shù)據(jù)訪問涯保。
通常诉濒,您在以下兩種情況之一中與內(nèi)容提供者合作;您可能想要實現(xiàn)代碼以訪問另一個應(yīng)用程序中的現(xiàn)有內(nèi)容提供者夕春,或者您可能希望在應(yīng)用程序中創(chuàng)建一個新的內(nèi)容提供者以與其他應(yīng)用程序共享數(shù)據(jù)未荒。本主題涵蓋與現(xiàn)有內(nèi)容提供者合作的基礎(chǔ)知識。要了解有關(guān)在自己的應(yīng)用程序中實現(xiàn)內(nèi)容提供者的更多信息及志,請參閱 創(chuàng)建內(nèi)容提供者片排。
本主題描述以下內(nèi)容:
- 內(nèi)容提供商的工作方式。
- 您用于從內(nèi)容提供商檢索數(shù)據(jù)的API速侈。
- 您用于在內(nèi)容提供程序中插入率寡,更新或刪除數(shù)據(jù)的API。
- 有助于與提供者合作的其他API功能倚搬。
總覽
內(nèi)容提供商將數(shù)據(jù)作為一個或多個表呈現(xiàn)給外部應(yīng)用程序冶共,這些表與關(guān)系數(shù)據(jù)庫中的表相似。行代表提供者收集的某種類型的數(shù)據(jù)的實例每界,并且行中的每一列代表為實例收集的單個數(shù)據(jù)捅僵。
內(nèi)容提供者針對許多不同的API和組件(如圖1所示)協(xié)調(diào)對應(yīng)用程序中數(shù)據(jù)存儲層的訪問,這些API和組件包括:
- 與其他應(yīng)用程序共享對您的應(yīng)用程序數(shù)據(jù)的訪問
- 將數(shù)據(jù)發(fā)送到小部件
- 通過使用以下內(nèi)容的搜索框架為您的應(yīng)用返回自定義搜索建議 SearchRecentSuggestionsProvider
- 使用的實現(xiàn)將應(yīng)用程序數(shù)據(jù)與服務(wù)器同步 AbstractThreadedSyncAdapter
- 使用以下命令在用戶界面中加載數(shù)據(jù) CursorLoader
圖1.內(nèi)容提供者與其他組件之間的關(guān)系盆犁。
訪問提供商
當(dāng)您要訪問內(nèi)容提供程序中的數(shù)據(jù)時命咐,可以使用 ContentResolver應(yīng)用程序中的對象Context作為客戶端與提供程序 進行通信。該 ContentResolver對象與提供者對象(實現(xiàn)的類的實例)進行通信ContentProvider谐岁。提供者對象從客戶端接收數(shù)據(jù)請求醋奠,執(zhí)行請求的操作,然后返回結(jié)果伊佃。該對象的方法調(diào)用提供程序?qū)ο螅ǖ囊粋€具體子類之一的實例)中具有相同名稱的方法ContentProvider窜司。這些 ContentResolver方法提供了持久性存儲的基本“ CRUD”(創(chuàng)建,檢索航揉,更新和刪除)功能塞祈。
ContentProvider從用戶界面 訪問a的常見模式是使用a CursorLoader在后臺運行異步查詢。您的用戶界面中的 Activity或Fragment調(diào)用 CursorLoader查詢帅涂,而查詢又 ContentProvider使用ContentResolver议薪。這允許用戶在查詢運行時繼續(xù)對用戶可用。這種模式涉及許多不同對象的交互以及底層存儲機制媳友,如圖2所示斯议。
圖2. ContentProvider,其他類和存儲之間的交互醇锚。
注意:要訪問提供程序哼御,您的應(yīng)用程序通常必須在其清單文件中請求特定的權(quán)限坯临。在內(nèi)容提供者權(quán)限一節(jié)中將詳細描述這種開發(fā)模式。
Android詞典中的內(nèi)置提供程序之一是用戶詞典恋昼,該詞典存儲用戶想要保留的非標準單詞的拼寫看靠。表1說明了此提供程序的表中的數(shù)據(jù)可能是什么樣的:
在表1中,每一行代表一個在標準字典中可能找不到的單詞的實例液肌。每列代表該單詞的一些數(shù)據(jù)挟炬,例如首次遇到該單詞的語言環(huán)境。列標題是存儲在提供程序中的列名矩屁。要引用行的語言環(huán)境辟宗,請引用其
locale
列。對于此提供程序吝秕,該_ID
列用作提供程序自動維護的“主鍵”列泊脐。
要從用戶詞典提供者那里獲取單詞及其語言環(huán)境的列表,請致電ContentResolver.query()烁峭。該query()方法調(diào)用ContentProvider.query()用戶詞典提供者定義的 方法容客。以下代碼行顯示了一個 ContentResolver.query()調(diào)用:
//查詢用戶字典并返回結(jié)果
// Queries the user dictionary and returns results
cursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // 單詞表的內(nèi)容URI
projection, // 每行要返回的列
selectionClause, // 選擇標準
selectionArgs, // 選擇標準
sortOrder); // 返回的行的排序順序
表2顯示了如何query(Uri,projection,selection,selectionArgs,sortOrder)匹配SQL SELECT語句的參數(shù) :
內(nèi)容URI
甲內(nèi)容URI是一個URI,在一個提供者識別數(shù)據(jù)约郁。內(nèi)容URI包括整個提供程序的符號名稱(它的權(quán)限)和指向表的名稱(路徑)缩挑。當(dāng)您調(diào)用客戶端方法來訪問提供程序中的表時,表的內(nèi)容URI是參數(shù)之一鬓梅。
在前面的代碼行中供置,該常量 CONTENT_URI包含用戶詞典的“單詞”表的內(nèi)容URI。該ContentResolver對象解析出URI的權(quán)限绽快,并通過將權(quán)限與已知提供程序的系統(tǒng)表進行比較來使用它來“解析”提供程序芥丧。然后, ContentResolver可以將查詢參數(shù)分派到正確的提供程序坊罢。
在ContentProvider使用內(nèi)容URI的路徑部分選擇表的訪問续担。提供程序通常為它公開的每個表都有一個路徑。
在前面的代碼行中活孩,“單詞”表的完整URI為:
content://user_dictionary/words
其中物遇,user_dictionary
字符串是提供者的權(quán)限,words
字符串是表的路徑憾儒。字符串 content:// (the scheme) 始終存在询兴,并將其標識為內(nèi)容URI。
許多提供程序都允許您通過將ID值附加到URI的末尾來訪問表中的一行起趾。例如蕉朵,要檢索行,其_ID
是 4
從用戶詞典阳掐,您可以使用此內(nèi)容的URI:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
當(dāng)您檢索一組行然后想要更新或刪除其中一個時,通常會使用id值。
注意:所述的Uri與Uri.Builder類包含用于從字符串構(gòu)建合式URI對象方便的方法缭保。本 ContentUris類包含附加ID值到URI方便的方法汛闸。上一個代碼段用于withAppendedId()將ID附加到UserDictionary內(nèi)容URI。
從提供者檢索數(shù)據(jù)
本節(jié)以用戶詞典提供程序為例艺骂,介紹如何從提供程序檢索數(shù)據(jù)诸老。
為了清楚起見,本節(jié)中的代碼段調(diào)用 ContentResolver.query()“ UI線程”钳恕。但是别伏,在實際代碼中,您應(yīng)該在單獨的線程上異步執(zhí)行查詢忧额。一種方法是使用CursorLoader類厘肮,即在更詳細地描述 裝填機。導(dǎo)向另外睦番,代碼行是唯一的代碼段;它們不表現(xiàn)出一個完整的應(yīng)用程序类茂。
要從提供程序檢索數(shù)據(jù),請按照以下基本步驟操作:
- 請求提供者的讀取訪問權(quán)限托嚣。
- 定義將查詢發(fā)送到提供程序的代碼巩检。
請求讀取訪問權(quán)限
要從提供程序檢索數(shù)據(jù),您的應(yīng)用程序需要提供程序的“讀取訪問權(quán)限”示启。您不能在運行時請求此權(quán)限兢哭。相反,您必須使用<uses-permission> 提供程序定義的元素和確切的權(quán)限名稱夫嗓,在清單中指定需要此權(quán)限 迟螺。當(dāng)您在清單中指定此元素時,實際上是在“請求”應(yīng)用程序的此權(quán)限啤月。用戶安裝您的應(yīng)用程序時煮仇,他們暗中同意此請求。
要查找您正在使用的提供程序的讀取訪問權(quán)限的確切名稱谎仲,以及提供程序使用的其他訪問權(quán)限的名稱浙垫,請參閱提供程序的文檔。
權(quán)限在訪問提供程序中的作用將在“ 內(nèi)容提供程序權(quán)限 ”部分中詳細介紹 郑诺。
用戶詞典提供程序android.permission.READ_USER_DICTIONARY
在其清單文件中定義了權(quán)限 夹姥,因此想要從提供程序讀取的應(yīng)用程序必須請求此權(quán)限。
構(gòu)造查詢
從提供程序檢索數(shù)據(jù)的下一步是構(gòu)造查詢辙诞。第一個代碼段定義了一些用于訪問User Dictionary Provider的變量:
//“ projection”定義將為每一行返回的列
字符串[] mProjection =
{
UserDictionary.Words._ID辙售,// _ID列名稱的合同類常量
UserDictionary.Words.WORD,//單詞列名稱的合同類常量
UserDictionary.Words.LOCALE //區(qū)域設(shè)置列名稱的合同類常量
};
//定義一個包含選擇子句的字符串
字符串selectionClause = null ;
//初始化一個數(shù)組以包含選擇參數(shù)
字符串[] selectionArgs = { “” };
下一個代碼段以ContentResolver.query()User Dictionary Provider為例飞涂,說明如何使用 旦部。提供程序客戶端查詢類似于SQL查詢祈搜,并且包含一組要返回的列,一組選擇條件和排序順序士八。
查詢應(yīng)返回的一組列稱為投影 (變量mProjection
)容燕。
指定要檢索的行的表達式分為選擇子句和選擇參數(shù)。選擇子句是邏輯和布爾表達式婚度,列名和值(變量mSelectionClause
)的組合蘸秘。如果指定可替換參數(shù)?
而不是值,則查詢方法將從選擇參數(shù)數(shù)組(變量mSelectionArgs
)中檢索值蝗茁。
在下一個代碼段中醋虏,如果用戶不輸入單詞,則選擇子句設(shè)置為 null
哮翘,查詢將返回提供程序中的所有單詞颈嚼。如果用戶輸入單詞,則選擇子句設(shè)置為UserDictionary.Words.WORD + " = ?"
忍坷,選擇參數(shù)數(shù)組的第一個元素設(shè)置為用戶輸入的單詞粘舟。
/*
* This defines a one-element String array to contain the selection argument.
*/
String[] selectionArgs = {""};
// Gets a word from the UI
searchString = searchWord.getText().toString();
// Remember to insert code here to check for invalid or malicious input.
// If the word is the empty string, gets everything
if (TextUtils.isEmpty(searchString)) {
// Setting the selection clause to null will return all words
selectionClause = null;
selectionArgs[0] = "";
} else {
// Constructs a selection clause that matches the word that the user entered.
selectionClause = UserDictionary.Words.WORD + " = ?";
// Moves the user's input string to the selection arguments.
selectionArgs[0] = searchString;
}
// Does a query against the table and returns a Cursor object
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
projection, // The columns to return for each row
selectionClause, // Either null, or the word the user entered
selectionArgs, // Either empty, or the string the user entered
sortOrder); // The sort order for the returned rows
// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
/*
* Insert code here to handle the error. Be sure not to use the cursor! You may want to
* call android.util.Log.e() to log this error.
*
*/
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {
/*
* Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
* an error. You may want to offer the user the option to insert a new row, or re-type the
* search term.
*/
} else {
// Insert code here to do something with the results
}
此查詢類似于SQL語句:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
在此SQL語句中,使用實際的列名代替合同類常量佩研。
防止惡意輸入
如果由內(nèi)容提供者管理的數(shù)據(jù)在SQL數(shù)據(jù)庫中柑肴,則將外部不受信任的數(shù)據(jù)包含到原始SQL語句中會導(dǎo)致SQL注入。
考慮以下選擇子句:
//通過將用戶輸入連接到列名來構(gòu)造選擇子句
字符串selectionClause = “ var =” + userInput ;
如果這樣做旬薯,則允許用戶將惡意SQL連接到您的SQL語句上晰骑。例如,用戶可以輸入“ nothing; DROP TABLE *;”绊序。為mUserInput
硕舆,這將導(dǎo)致選擇子句var = nothing; DROP TABLE *;
。由于選擇子句被視為SQL語句骤公,因此這可能會導(dǎo)致提供程序擦除底層SQLite數(shù)據(jù)庫中的所有表(除非提供程序設(shè)置為捕獲 SQL注入嘗試)抚官。
為避免此問題,請使用選擇子句阶捆,該子句?
用作可替換參數(shù)和單獨的選擇參數(shù)數(shù)組凌节。執(zhí)行此操作時,用戶輸入直接綁定到查詢洒试,而不是被解釋為SQL語句的一部分倍奢。由于未將其視為SQL,因此用戶輸入無法注入惡意SQL垒棋。代替使用串聯(lián)來包含用戶輸入卒煞,請使用以下選擇子句:
//構(gòu)造一個帶有可替換參數(shù)的選擇子句
字符串選擇Clause =“ var =?”;
設(shè)置選擇參數(shù)數(shù)組叼架,如下所示:
//定義一個包含選擇參數(shù)的數(shù)組
字符串[] selectionArgs = {“”};
?即使提供者不是基于SQL數(shù)據(jù)庫畔裕,也 可以使用用作可替換參數(shù)的選擇子句和選擇參數(shù)數(shù)組的數(shù)組來指定選擇衣撬。
顯示查詢結(jié)果
的ContentResolver.query()客戶端方法總是返回一個Cursor含有由查詢的投影為匹配查詢的選擇標準的行指定的列。一個 Cursor對象提供它所包含的行和列的隨機讀取訪問柴钻。使用Cursor方法淮韭,您可以遍歷結(jié)果中的行,確定每列的數(shù)據(jù)類型贴届,從列中獲取數(shù)據(jù),并檢查結(jié)果的其他屬性蜡吧。某些Cursor實現(xiàn)在提供者的數(shù)據(jù)發(fā)生更改時自動更新對象毫蚓,或在更改發(fā)生時自動觸發(fā)觀察者對象中的方法Cursor,或兩者同時發(fā)生昔善。
注意:提供程序可能會根據(jù)進行查詢的對象的性質(zhì)來限制對列的訪問元潘。例如,Contacts Provider限制對某些列的訪問以同步適配器君仆,因此它不會將它們返回到活動或服務(wù)翩概。
如果沒有行符合選擇條件,則提供程序?qū)⒎祷匾粋€Cursor對象返咱,該對象的 Cursor.getCount()值為0(空游標)钥庇。
如果發(fā)生內(nèi)部錯誤,則查詢結(jié)果取決于特定的提供程序咖摹。它可能選擇返回null
评姨,也可能會拋出一個Exception。
由于a Cursor是行的“列表”萤晴,因此顯示a的內(nèi)容的一種好方法Cursor是ListView 通過a 將其鏈接到a SimpleCursorAdapter吐句。
以下代碼段延續(xù)了上一個代碼段的代碼。它創(chuàng)建一個 SimpleCursorAdapter包含Cursor 查詢檢索到的對象店读,并將該對象設(shè)置為的適配器 ListView:
// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] wordListColumns =
{
UserDictionary.Words.WORD, // Contract class constant containing the word column name
UserDictionary.Words.LOCALE // Contract class constant containing the locale column name
};
// Defines a list of View IDs that will receive the Cursor columns for each row
int[] wordListItems = { R.id.dictWord, R.id.locale};
// Creates a new SimpleCursorAdapter
cursorAdapter = new SimpleCursorAdapter(
getApplicationContext(), // The application's Context object
R.layout.wordlistrow, // A layout in XML for one row in the ListView
mCursor, // The result from the query
wordListColumns, // A string array of column names in the cursor
wordListItems, // An integer array of view IDs in the row layout
0); // Flags (usually none are needed)
// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter);
注意:要使用來ListView支持a Cursor嗦枢,光標必須包含名為的列_ID
。因此屯断,先前顯示的查詢將檢索_ID
“單詞”表的列文虏,即使該表ListView未顯示該列。此限制還解釋了為什么大多數(shù)提供程序_ID
的每個表都有一個列裹纳。
從查詢結(jié)果中獲取數(shù)據(jù)
您不僅可以顯示查詢結(jié)果择葡,還可以將其用于其他任務(wù)。例如剃氧,您可以從用戶詞典中檢索拼寫敏储,然后在其他提供程序中查找它們。為此朋鞍,您需要遍歷以下行Cursor:
// Determine the column index of the column named "word"
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
/*
* Only executes if the cursor is valid. The User Dictionary Provider returns null if
* an internal error occurs. Other providers may throw an Exception instead of returning null.
*/
if (mCursor != null) {
/*
* Moves to the next row in the cursor. Before the first movement in the cursor, the
* "row pointer" is -1, and if you try to retrieve data at that position you will get an
* exception.
*/
while (mCursor.moveToNext()) {
// Gets the value from the column.
newWord = mCursor.getString(index);
// Insert code here to process the retrieved word.
...
// end of while loop
}
} else {
// Insert code here to report an error if the cursor is null or the provider threw an exception.
}
Cursor實現(xiàn)包含幾個“獲取”方法已添,用于從對象中檢索不同類型的數(shù)據(jù)妥箕。例如,先前的代碼段使用getString()更舞。它們還具有一個getType()返回值的 方法畦幢,該值指示列的數(shù)據(jù)類型。
內(nèi)容提供商權(quán)限
提供者的應(yīng)用程序可以指定其他應(yīng)用程序必須具有的權(quán)限才能訪問提供者的數(shù)據(jù)缆蝉。這些權(quán)限可確保用戶知道應(yīng)用程序?qū)L試訪問哪些數(shù)據(jù)宇葱。根據(jù)提供商的要求,其他應(yīng)用程序會請求他們所需的權(quán)限才能訪問提供商刊头。最終用戶在安裝應(yīng)用程序時會看到請求的權(quán)限黍瞧。
如果提供程序的應(yīng)用程序未指定任何權(quán)限,則其他應(yīng)用程序?qū)o權(quán)訪問提供程序的數(shù)據(jù)原杂。但是印颤,無論指定的權(quán)限如何,提供者的應(yīng)用程序中的組件始終具有完全的讀取和寫入訪問權(quán)限穿肄。
如前所述年局,用戶詞典提供者需要android.permission.READ_USER_DICTIONARY
獲得從中檢索數(shù)據(jù)的 權(quán)限。提供者具有android.permission.WRITE_USER_DICTIONARY
插入咸产,更新或刪除數(shù)據(jù)的單獨權(quán)限矢否。
為了獲得訪問提供程序所需的權(quán)限,應(yīng)用程序會<uses-permission> 在清單文件中使用元素來請求它們 锐朴。當(dāng)Android Package Manager安裝該應(yīng)用程序時兴喂,用戶必須批準該應(yīng)用程序請求的所有權(quán)限。如果用戶全部批準焚志,則程序包管理器將繼續(xù)安裝衣迷;否則,將繼續(xù)安裝酱酬。如果用戶不同意壶谒,包管理器將中止安裝。
以下 <uses-permission> 元素請求對用戶詞典提供者的讀取訪問權(quán)限:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
權(quán)限對提供程序訪問的影響將在“ 安全和權(quán)限”指南中進行詳細說明 膳沽。
插入汗菜,更新和刪除數(shù)據(jù)
與從提供程序檢索數(shù)據(jù)的方式相同,您還可以使用提供程序客戶端與提供程序的客戶端之間的交互ContentProvider來修改數(shù)據(jù)挑社。您調(diào)用ContentResolver帶有傳遞給的相應(yīng)方法的參數(shù)的方法ContentProvider陨界。提供者和提供者客戶端自動處理安全性和進程間通信。
插入資料
要將數(shù)據(jù)插入提供程序痛阻,請調(diào)用 ContentResolver.insert() 方法菌瘪。此方法將新行插入提供程序,并返回該行的內(nèi)容URI。此代碼段顯示了如何在User Dictionary Provider中插入新單詞:
// Defines a new Uri object that receives the result of the insertion
Uri newUri;
...
// Defines an object to contain the new values to insert
ContentValues newValues = new ContentValues();
/*
* Sets the values of each column and inserts the word. The arguments to the "put"
* method are "column name" and "value"
*/
newValues.put(UserDictionary.Words.APP_ID, "example.user");
newValues.put(UserDictionary.Words.LOCALE, "en_US");
newValues.put(UserDictionary.Words.WORD, "insert");
newValues.put(UserDictionary.Words.FREQUENCY, "100");
newUri = getContentResolver().insert(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
newValues // the values to insert
);
新行的數(shù)據(jù)將進入一個ContentValues對象俏扩,該對象的形式類似于單行游標糜工。該對象中的列不必具有相同的數(shù)據(jù)類型,并且如果您根本不想指定值录淡,則可以將列設(shè)置為null
using ContentValues.putNull()捌木。
該代碼段不會添加該_ID
列,因為該列是自動維護的嫉戚。提供程序?qū)⑽ㄒ坏闹捣峙浣o_ID
添加的每一行刨裆。提供程序通常將此值用作表的主鍵。
返回的內(nèi)容URI newUri
標識新添加的行彬檀,格式如下:
content://user_dictionary/words/<id_value>
的<id_value>
是內(nèi)容的_ID
新行崔拥。大多數(shù)提供程序可以自動檢測這種形式的內(nèi)容URI,然后在該特定行上執(zhí)行請求的操作凤覆。
要從_ID
返回的值中獲取的值Uri,請調(diào)用 ContentUris.parseId()拆魏。
更新數(shù)據(jù)
要更新行盯桦,可以像使用ContentValues插入一樣使用具有更新值的對象,也可以像查詢一樣使用選擇條件渤刃。您使用的客戶端方法是 ContentResolver.update()拥峦。您只需要為ContentValues要更新的列的對象添加值。如果要清除列的內(nèi)容卖子,請將值設(shè)置為null
略号。
以下代碼段將語言環(huán)境為“ en”的所有行更改為語言環(huán)境為的行null
。返回值是已更新的行數(shù):
// Defines an object to contain the updated values
ContentValues updateValues = new ContentValues();
// Defines selection criteria for the rows you want to update
String selectionClause = UserDictionary.Words.LOCALE + " LIKE ?";
String[] selectionArgs = {"en_%"};
// Defines a variable to contain the number of updated rows
int rowsUpdated = 0;
...
/*
* Sets the updated value and updates the selected words.
*/
updateValues.putNull(UserDictionary.Words.LOCALE);
rowsUpdated = getContentResolver().update(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
updateValues, // the columns to update
selectionClause, // the column to select on
selectionArgs // the value to compare to
);
調(diào)用時洋闽,還應(yīng)該清理用戶輸入 ContentResolver.update()玄柠。要了解更多信息,請閱讀防止惡意輸入部分诫舅。
刪除資料
刪除行類似于檢索行數(shù)據(jù):您為要刪除的行指定選擇標準羽利,并且客戶端方法返回已刪除行的數(shù)量。以下代碼段刪除其appid與“ user”匹配的行刊懈。該方法返回已刪除的行數(shù)这弧。
//為要刪除的行定義選擇條件
字符串selectionClause = UserDictionary.Words.APP_ID +“ LIKE?”;
字符串[] selectionArgs = { “ user” };
//定義一個變量以包含已刪除的行數(shù)
int rowsDeleted = 0 ;
...
//刪除符合選擇條件的單詞
rowsDeleted = getContentResolver ().delete(
UserDictionary.Words.CONTENT_URI 虚汛,//用戶字典內(nèi)容URI
selectionClause 匾浪,//要選擇的列
selectionArgs //要比較的值
);
調(diào)用時,還應(yīng)該清理用戶輸入 ContentResolver.delete()卷哩。要了解更多信息蛋辈,請閱讀防止惡意輸入部分。
提供者數(shù)據(jù)類型
內(nèi)容提供商可以提供許多不同的數(shù)據(jù)類型殉疼。用戶詞典提供程序僅提供文本梯浪,但是提供程序也可以提供以下格式:
- 整數(shù)
- 長整數(shù)(長)
- 浮點
- 長浮點數(shù)(雙精度)
提供程序經(jīng)常使用的另一種數(shù)據(jù)類型是實現(xiàn)為64KB字節(jié)數(shù)組的二進制大對象(BLOB)捌年。您可以通過查看Cursor類“ get”方法來查看可用的數(shù)據(jù)類型 。
提供程序中每一列的數(shù)據(jù)類型通常會在其文檔中列出挂洛。用戶詞典提供者的數(shù)據(jù)類型在其合同類別的參考文檔中列出UserDictionary.Words(合同類別在“ 合同類別 ”部分中進行了描述)礼预。您還可以通過調(diào)用來確定數(shù)據(jù)類型Cursor.getType()。
提供程序還為他們定義的每個內(nèi)容URI維護MIME數(shù)據(jù)類型信息虏劲。您可以使用MIME類型信息來確定您的應(yīng)用程序是否可以處理提供程序提供的數(shù)據(jù)托酸,或者根據(jù)MIME類型選擇處理類型。當(dāng)您使用包含復(fù)雜數(shù)據(jù)結(jié)構(gòu)或文件的提供程序時柒巫,通常需要MIME類型励堡。例如,ContactsContract.Data Contacts Provider中的表使用MIME類型來標記存儲在每一行中的聯(lián)系人數(shù)據(jù)的類型堡掏。要獲取與內(nèi)容URI對應(yīng)的MIME類型应结,請調(diào)用 ContentResolver.getType()。
MIME類型參考 部分介紹了標準MIME類型和自定義MIME類型的語法泉唁。
提供者訪問的替代形式
提供者訪問的三種替代形式在應(yīng)用程序開發(fā)中很重要:
- 批量訪問:您可以使用ContentProviderOperation類中的方法創(chuàng)建一批訪問調(diào)用鹅龄,然后使用進行應(yīng)用 ContentResolver.applyBatch()。
- 異步查詢:您應(yīng)該在單獨的線程中進行查詢亭畜。一種方法是使用CursorLoader對象扮休。加載程序指南中的示例 演示了如何執(zhí)行此操作。
- 通過意向訪問數(shù)據(jù):盡管您不能直接向提供者發(fā)送意圖拴鸵,但是您可以將意圖發(fā)送到提供者的應(yīng)用程序玷坠,這通常是修改提供者數(shù)據(jù)的最佳裝備。
以下各節(jié)介紹了通過意向進行的批量訪問和修改劲藐。
批量訪問
對提供程序的批訪問對于插入大量行或在同一方法調(diào)用中的多個表中插入行非常有用八堡,或者通常用于跨事務(wù)邊界執(zhí)行一組作為事務(wù)的操作(原子操作)。
要以“批處理模式”訪問提供程序瘩燥,請創(chuàng)建一個ContentProviderOperation對象數(shù)組秕重,然后使用分配它們到內(nèi)容提供程序 ContentResolver.applyBatch()。您將內(nèi)容提供者的權(quán)限傳遞給此方法厉膀,而不是特定的內(nèi)容URI溶耘。這允許ContentProviderOperation數(shù)組中的每個對象針對不同的表工作。調(diào)用將ContentResolver.applyBatch()返回結(jié)果數(shù)組服鹅。
ContactsContract.RawContacts合同類 的描述包括演示批處理插入的代碼段凳兵。該 聯(lián)系人管理器 示例應(yīng)用程序包含在其批量訪問的示例ContactAdder.java
源文件。
通過意圖進行數(shù)據(jù)訪問
意圖可以提供對內(nèi)容提供商的間接訪問企软。即使您的應(yīng)用程序沒有訪問權(quán)限庐扫,您也可以允許用戶訪問提供程序中的數(shù)據(jù),方法是從具有許可權(quán)的應(yīng)用程序獲取結(jié)果意圖,或者通過激活具有許可權(quán)的應(yīng)用程序并讓用戶在其中工作它形庭。
獲得臨時權(quán)限的訪問
即使沒有適當(dāng)?shù)脑L問權(quán)限铅辞,也可以通過將意圖發(fā)送給具有許可權(quán)的應(yīng)用程序并接收回包含“ URI”許可權(quán)的結(jié)果意圖,來訪問內(nèi)容提供程序中的數(shù)據(jù)萨醒。這些是對特定內(nèi)容URI的許可斟珊,這些許可將持續(xù)到接收它們的活動完成為止。具有永久權(quán)限的應(yīng)用程序通過在結(jié)果意圖中設(shè)置標志來授予臨時權(quán)限:
- 閱讀權(quán)限: FLAG_GRANT_READ_URI_PERMISSION
- 寫入權(quán)限: FLAG_GRANT_WRITE_URI_PERMISSION
注意:這些標志不授予對權(quán)限包含在內(nèi)容URI中的提供程序的常規(guī)讀取或?qū)懭朐L問富纸。該訪問僅針對URI本身囤踩。
提供者使用 元素的android:grantUriPermission 屬性以及 <provider>元素的 <grant-uri-permission> 子元素 為其清單中的內(nèi)容URI定義URI權(quán)限 <provider> 。URI權(quán)限機制在“ 權(quán)限”概述指南中有更詳細的 說明晓褪。
例如堵漱,即使您沒有READ_CONTACTS權(quán)限,也可以在“聯(lián)系人提供程序”中檢索聯(lián)系人的數(shù)據(jù)涣仿。您可能想要在向生日當(dāng)天的聯(lián)系人發(fā)送電子問候的應(yīng)用程序中執(zhí)行此操作勤庐。READ_CONTACTS您可以選擇讓用戶控制您的應(yīng)用程序使用哪些聯(lián)系人,而不是請求好港,后者使您可以訪問所有用戶的聯(lián)系人及其所有信息埃元。為此,您使用以下過程:
- 你的應(yīng)用程序發(fā)送一個意圖包含操作 ACTION_PICK和“聯(lián)系人” MIME類型 CONTENT_ITEM_TYPE媚狰,使用方法startActivityForResult()。
- 由于此意圖與People應(yīng)用程序的“選擇”活動的意圖過濾器匹配阔拳,因此該活動將成為前臺崭孤。
- 在選擇活動中,用戶選擇要更新的聯(lián)系人糊肠。發(fā)生這種情況時辨宠,選擇活動將調(diào)用 setResult(resultcode, intent) 以建立回饋給您的應(yīng)用程序的意圖。該意圖包含用戶選擇的聯(lián)系人的內(nèi)容URI和“額外”標志 FLAG_GRANT_READ_URI_PERMISSION货裹。這些標志向您的應(yīng)用授予URI權(quán)限嗤形,以讀取內(nèi)容URI指向的聯(lián)系人的數(shù)據(jù)。然后弧圆,選擇活動調(diào)用finish()以將控制權(quán)返回給您的應(yīng)用程序赋兵。
- 您的活動返回到前臺,然后系統(tǒng)調(diào)用您活動的 onActivityResult() 方法搔预。此方法接收由“人脈”應(yīng)用中的選擇活動創(chuàng)建的結(jié)果意圖霹期。
- 使用結(jié)果意圖中的內(nèi)容URI,即使您未在清單中請求對提供者的永久讀取訪問權(quán)限拯田,也可以從“聯(lián)系提供者”中讀取聯(lián)系人的數(shù)據(jù)历造。然后,您可以獲取聯(lián)系人的生日信息或他們的電子郵件地址,然后發(fā)送電子問候吭产。
使用其他應(yīng)用程序
允許用戶修改您沒有訪問權(quán)限的數(shù)據(jù)的一種簡單方法是激活具有權(quán)限的應(yīng)用程序侣监,然后讓用戶在其中進行工作。
例如臣淤,“日歷”應(yīng)用程序接受一個 ACTION_INSERT意圖橄霉,該意圖使您可以激活應(yīng)用程序的插入UI。您可以以此意圖傳遞“額外”數(shù)據(jù)荒典,應(yīng)用程序?qū)⑵溆糜陬A(yù)填充UI酪劫。由于重復(fù)發(fā)生的事件具有復(fù)雜的語法,因此將事件插入“日歷提供程序”的首選方法是使用激活日歷應(yīng)用程序寺董, ACTION_INSERT然后讓用戶在其中插入事件覆糟。
使用助手應(yīng)用程序顯示數(shù)據(jù)
如果您的應(yīng)用程序確實具有訪問權(quán)限,則您可能仍想使用一種意圖在另一個應(yīng)用程序中顯示數(shù)據(jù)遮咖。例如滩字,“日歷”應(yīng)用程序接受一個 ACTION_VIEW意圖,該意圖顯示特定的日期或事件御吞。這使您可以顯示日歷信息麦箍,而不必創(chuàng)建自己的UI。要了解有關(guān)此功能的更多信息陶珠,請參閱“ 日歷提供程序”指南挟裂。
您向其發(fā)送意圖的應(yīng)用程序不必是與提供程序關(guān)聯(lián)的應(yīng)用程序。例如揍诽,您可以從“聯(lián)系人提供者”中檢索聯(lián)系人诀蓉,然后將ACTION_VIEW包含聯(lián)系人圖像的內(nèi)容URI 的意圖發(fā)送給圖像查看器。
合約類別
合同類定義用于幫助應(yīng)用程序處理內(nèi)容提供??者的內(nèi)容URI暑脆,列名渠啤,意圖動作和其他功能的常量。提供商不自動包含合同類別添吗;提供者的開發(fā)人員必須定義它們沥曹,然后將其提供給其他開發(fā)人員。Android平臺隨附的許多提供程序在軟件包中都有相應(yīng)的合同類android.provider碟联。
例如妓美,用戶詞典提供者具有UserDictionary包含內(nèi)容URI和列名常量的協(xié)定類 ±鸱酰“ words”表的內(nèi)容URI在constant中定義 UserDictionary.Words.CONTENT_URI部脚。的UserDictionary.Words類還包含列名常量,其在該示例段所使用本指南英寸 例如裤纹,查詢投影可以定義為:
String[] projection =
{
UserDictionary.Words._ID,
UserDictionary.Words.WORD,
UserDictionary.Words.LOCALE
};
另一個合同類別ContactsContract適用于聯(lián)系人提供者躏鱼。此類的參考文檔包括示例代碼段。它的子類之一ContactsContract.Intents.Insert是合同類癞季,其中包含意圖和意圖數(shù)據(jù)的常量。
MIME類型參考
內(nèi)容提供者可以返回標準MIME媒體類型或自定義MIME類型字符串呕童,或兩者都返回。
MIME類型具有以下格式
type/subtype
例如淆珊,眾所周知的MIME類型text/html具有text類型和html子類型夺饲。如果提供程序為URI返回此類型,則意味著使用該URI的查詢將返回包含HTML標記的文本施符。
自定義MIME類型字符串往声,也稱為“供應(yīng)商特定的” MIME類型,具有更復(fù)雜的類型和子類型值戳吝。該類型的值總是
vnd.android.cursor.dir
對于多行浩销,或
vnd.android.cursor.item
單行。
該亞型是供應(yīng)商特定的听哭。Android內(nèi)置提供程序通常具有一個簡單的子類型慢洋。例如,當(dāng)“聯(lián)系人”應(yīng)用程序為電話號碼創(chuàng)建一行時陆盘,它將在該行中設(shè)置以下MIME類型:
vnd.android.cursor.item/phone_v2
請注意普筹,子類型的值只是phone_v2。
其他提供程序開發(fā)人員可以根據(jù)提供程序的權(quán)限和表名稱創(chuàng)建自己的子類型模式隘马。例如太防,考慮包含列車時刻表的提供商。提供者的權(quán)限是com.example.trains酸员,并且包含表Line1杏头,Line2和Line3。響應(yīng)內(nèi)容URI
content://com.example.trains/Line1
對于表Line1沸呐,提供程序返回MIME類型
vnd.android.cursor.dir/vnd.example.line1
響應(yīng)內(nèi)容URI
content://com.example.trains/Line2/5
對于表Line2中的第5行,提供程序返回MIME類型
vnd.android.cursor.item/vnd.example.line2
大多數(shù)內(nèi)容提供者為其使用的MIME類型定義合同類常量呢燥。ContactsContract.RawContacts例如崭添,Contacts Provider合同類定義CONTENT_ITEM_TYPE了單個原始聯(lián)系人行的MIME類型的常量 。
內(nèi)容URI部分中描述了單行的 內(nèi)容URI叛氨。