Android内容提供程序
內(nèi)容提供程序管理對結(jié)構(gòu)化數(shù)據(jù)集的訪問,它們封裝數(shù)據(jù),并供用于定義數(shù)據(jù)安全性的機(jī)制。內(nèi)容提供程序是連接一個進(jìn)程中的數(shù)據(jù)與另一個進(jìn)程中運(yùn)行的代碼的標(biāo)準(zhǔn)界面。
將應(yīng)用的Context中的ContentResolver對象用作客戶端來提供程序通信,可以訪問內(nèi)容提供程序中的數(shù)據(jù)。ContentResolver對象會實現(xiàn)ContentProvider的類實例通信,提供程序?qū)ο髲目蛻舳私邮諗?shù)據(jù)請求,執(zhí)行請求的操作并返回結(jié)果。
內(nèi)容提供程序以一個或多個表的形式將數(shù)據(jù)呈現(xiàn)給外部應(yīng)用,行表示提供程序收集的某種數(shù)據(jù)類型的實例,行中的每個列表示為實例收集的。
訪問提供程序
應(yīng)用從具有ContentResolver客戶端對象的內(nèi)容提供程序訪問數(shù)據(jù),此對象具有調(diào)用提供程序?qū)ο笾型椒ǖ姆椒?#xff0c;ContentResolver方法可提供程序存儲的基本CRUD(創(chuàng)建,檢索,更新,刪除)功能。客戶端應(yīng)用進(jìn)程中ContentResolver對象和擁有提供程序的應(yīng)用中的ContentProvider對象可自動處理跨進(jìn)程通信,ContentProvider還可充當(dāng)其數(shù)據(jù)存儲區(qū)和表格外部顯示之間的抽象層。
內(nèi)容URI
是用于在提供程序中表示數(shù)據(jù)的URI,包括整個提供程序的符號名稱,和一個指向表的名稱(路徑)。ContentResolver對象會分析出URI的授權(quán),并通過將該授權(quán)與已知提供程序的系統(tǒng)進(jìn)行比較來解析提供程序,然后,ContentResolver可以將查血的參數(shù)分派給正確的提供程序。
ContentProvider使用內(nèi)容URI的路徑部分來選擇要訪問的表,提供程序通常會為其公開每個表顯示一條路徑。
例如:content://user_dictionary/words
其中user_dictionary字符串是提供程序的授權(quán),words字符串是表的路徑,字符串content://(架構(gòu))始終顯示,并將此標(biāo)識為內(nèi)容URI
許多提供程序都允許通過將ID值追加到URI末尾來訪問表中的單個行。在檢索到一組行后想要更新或刪除其中某一行時通常用到ID值。
Uri和Uri.Builder 類包含根據(jù)字符串構(gòu)建格式規(guī)范的URI對象的便利方法,ContentUris包含一些可以將ID值追加到URI后的方法。例如:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4);
提供程序檢索數(shù)據(jù)
為了說明,本例在UI線程上調(diào)用ContentResolver.query(),但在實際代碼中應(yīng)該在單獨(dú)線程上異步執(zhí)行查詢,使用CursorLoader類,按照以下兩個基本操作執(zhí)行
1.請求對提供程序的讀取訪問權(quán)限
2.定義將查詢發(fā)送至提供程序的代碼
在清單文件<uses-permission>中添加讀取訪問權(quán)限
構(gòu)建查詢
// A "projection" defines the columns that will be returned for each row String[] mProjection = {UserDictionary.Words._ID, // Contract class constant for the _ID column nameUserDictionary.Words.WORD, // Contract class constant for the word column nameUserDictionary.Words.LOCALE // Contract class constant for the locale column name };// Defines a string to contain the selection clause String mSelectionClause = null;// Initializes an array to contain selection arguments String[] mSelectionArgs = {""};查詢應(yīng)該返回的列集被稱為投影(即變量mProjection)String[] mSelectionArgs = {""};// Gets a word from the UI mSearchString = mSearchWord.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(mSearchString)) {// Setting the selection clause to null will return all wordsmSelectionClause = null;mSelectionArgs[0] = "";} else {// Constructs a selection clause that matches the word that the user entered.mSelectionClause = UserDictionary.Words.WORD + " = ?";// Moves the user's input string to the selection arguments.mSelectionArgs[0] = mSearchString;}// Does a query against the table and returns a Cursor object mCursor = getContentResolver().query(UserDictionary.Words.CONTENT_URI, // The content URI of the words tablemProjection, // The columns to return for each rowmSelectionClause // Either null, or the word the user enteredmSelectionArgs, // Either empty, or the string the user enteredmSortOrder); // 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}如果用戶未輸入字詞,則選擇子句將設(shè)置為? null ,而且查詢會返回提供程序中的所有字詞。 如果用戶輸入了字詞,選擇子句將設(shè)置為? UserDictionary.Words.WORD + " = ?"?且選擇參數(shù)數(shù)組的第一個元素將設(shè)置為用戶輸入的字詞。
防止惡意輸入
如果內(nèi)容提供程序管理的數(shù)據(jù)位于 SQL 數(shù)據(jù)庫中,將不受信任的外部數(shù)據(jù)包括在原始 SQL 語句中可能會導(dǎo)致 SQL 注入。
要避免此問題,可使用一個用于將? ??作為可替換參數(shù)的選擇子句以及一個單獨(dú)的選擇參數(shù)數(shù)組。 執(zhí)行此操作時,用戶輸入直接受查詢約束,而不解釋為 SQL 語句的一部分。 由于用戶輸入未作為 SQL 處理,因此無法注入惡意 SQL。
String mSelectionClause = "var = ?"; String[] selectionArgs = {""}; selectionArgs[0] = mUserInput;
一個用于將???用作可替換參數(shù)的選擇子句和一個選擇參數(shù)數(shù)組是指定選擇的首選方式,即使提供程序并未基于 SQL 數(shù)據(jù)庫。
顯示查詢結(jié)果 ContentResolver.query()客戶端方法始終會返回符合以下條件的Cursor 包含查詢的投影為匹配查詢選擇條件的行指定的列.Cursor 對象為其包含的行和列提供隨機(jī)讀取訪問權(quán)限。 通過使用Cursor 方法,您可以循環(huán)訪問結(jié)果中的行、確定每個列的數(shù)據(jù)類型、從列中獲取數(shù)據(jù),并檢查結(jié)果的其他屬性。 某些Cursor 實現(xiàn)會在提供程序的數(shù)據(jù)發(fā)生更改時自動更新對象和/或在Cursor 更改時觸發(fā)觀察程序?qū)ο笾械姆椒ā?/span>如果沒有與選擇條件匹配的行,則提供程序會返回Cursor.geeCount()?為 0(空游標(biāo))的Cursor?對象。
如果出現(xiàn)內(nèi)部錯誤,查詢結(jié)果將取決于具體的提供程序。它可能會選擇返回?null,或引發(fā)Exception
由于Cursor是行“列表”,因此顯示Cursor?內(nèi)容的良好方式是通過SimpleCursorAdapter將其與ListView?關(guān)聯(lián)。
內(nèi)容提供程序權(quán)限提供程序的應(yīng)用可以指定其他應(yīng)用訪問提供程序的數(shù)據(jù)所必需的權(quán)限。 這些權(quán)限可確保用戶了解應(yīng)用將嘗試訪問的數(shù)據(jù)。 根據(jù)提供程序的要求,其他應(yīng)用會請求它們訪問提供程序所需的權(quán)限。 最終用戶會在安裝應(yīng)用時看到所請求的權(quán)限。
如果提供程序的應(yīng)用未指定任何權(quán)限,則其他應(yīng)用將無權(quán)訪問提供程序的數(shù)據(jù)。 但是,無論指定權(quán)限為何,提供程序的應(yīng)用中的組件始終具有完整的讀取和寫入訪問權(quán)限。
要獲取訪問提供程序所需的權(quán)限,應(yīng)用將通過其清單文件中的<uses-permission> 元素來請求這些權(quán)限。Android 軟件包管理器安裝應(yīng)用時,用戶必須批準(zhǔn)該應(yīng)用請求的所有權(quán)限。 如果用戶批準(zhǔn)所有權(quán)限,軟件包管理器將繼續(xù)安裝;如果用戶未批準(zhǔn)這些權(quán)限,軟件包管理器將中止安裝。插入、更新、刪除數(shù)據(jù)
與從提供程序檢索數(shù)據(jù)的方式相同,也可以通過提供程序客戶端和提供程序ContentProvider之間的交互來修改數(shù)據(jù)。 您通過傳遞到ContentProvider的對應(yīng)方法的參數(shù)來調(diào)用 ContentResolver方法。 提供程序和提供程序客戶端會自動處理安全性和跨進(jìn)程通信。
插入: // Defines a new Uri object that receives the result of the insertion Uri mNewUri;...// Defines an object to contain the new values to insert ContentValues mNewValues = new ContentValues();/** Sets the values of each column and inserts the word. The arguments to the "put"* method are "column name" and "value"*/ mNewValues.put(UserDictionary.Words.APP_ID, "example.user"); mNewValues.put(UserDictionary.Words.LOCALE, "en_US"); mNewValues.put(UserDictionary.Words.WORD, "insert"); mNewValues.put(UserDictionary.Words.FREQUENCY, "100");mNewUri = getContentResolver().insert(UserDictionary.Word.CONTENT_URI, // the user dictionary content URImNewValues // the values to insert );更新: // Defines an object to contain the updated values ContentValues mUpdateValues = new ContentValues();// Defines selection criteria for the rows you want to update String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?"; String[] mSelectionArgs = {"en_%"};// Defines a variable to contain the number of updated rows int mRowsUpdated = 0;.../** Sets the updated value and updates the selected words.*/ mUpdateValues.putNull(UserDictionary.Words.LOCALE);mRowsUpdated = getContentResolver().update(UserDictionary.Words.CONTENT_URI, // the user dictionary content URImUpdateValues // the columns to updatemSelectionClause // the column to select onmSelectionArgs // the value to compare to );刪除: // Defines selection criteria for the rows you want to delete String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] mSelectionArgs = {"user"};// Defines a variable to contain the number of rows deleted int mRowsDeleted = 0;...// Deletes the words that match the selection criteria mRowsDeleted = getContentResolver().delete(UserDictionary.Words.CONTENT_URI, // the user dictionary content URImSelectionClause // the column to select onmSelectionArgs // the value to compare to );提供程序訪問的替代形式 批量訪問:可以通過ContentProviderOperation類中的方法創(chuàng)建一批訪問調(diào)用,然后通過ContentResolver.applyBatch()應(yīng)用它們。 異步查詢:應(yīng)該在單獨(dú)線程中執(zhí)行查詢,使用CursorLoader對象。 通過Intent訪問數(shù)據(jù):盡管無法直接向提供程序發(fā)送Intent,但可以向提供程序的應(yīng)用發(fā)送Intent,后者通常具有修改提供程序數(shù)據(jù)的最佳配置。 協(xié)定類 協(xié)定類定義幫助應(yīng)用使用內(nèi)容 URI、列名稱、 Intent 操作以及內(nèi)容提供程序的其他功能的常量。 協(xié)定類未自動包含在提供程序中;提供程序的開發(fā)者需要定義它們,然后使其可用于其他開發(fā)者。 Android 平臺中包含的許多提供程序都在軟件包android.provider中具有對應(yīng)的協(xié)定類。總結(jié)
以上是生活随笔為你收集整理的Android内容提供程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Service中的绑定服务总结
- 下一篇: Android应用小工具(窗口小部件)