Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Content Providers基礎

Android Content Providers基礎

編輯:關於Android編程

Content Provider介紹


內容提供程序管理對結構化數據集的訪問。它們封裝數據,並提供用於定義數據安全性的機制。 內容提供程序是連接一個進程中的數據與另一個進程中運行的代碼的標准界面。

如果您想要訪問內容提供程序中的數據,可以將應用的 Context 中的 ContentResolver 對象用作客戶端來與提供程序通信。 ContentResolver 對象會與提供程序對象(即實現 ContentProvider 的類實例)通信。 提供程序對象從客戶端接收數據請求,執行請求的操作並返回結果。

如果您不打算與其他應用共享數據,則無需開發自己的提供程序。 不過,您需要通過自己的提供程序在您自己的應用中提供自定義搜索建議。 如果您想將復雜的數據或文件從您的應用復制並粘貼到其他應用中,也需要創建您自己的提供程序。

Android 本身包括的內容提供程序可管理音頻、視頻、圖像和個人聯系信息等數據。 android.provider 軟件包參考文檔中列出了部分提供程序。 任何 Android 應用都可以訪問這些提供程序,但會受到某些限制。

Content Provider基礎知識


內容提供程序管理對中央數據存儲庫的訪問。提供程序是 Android 應用的一部分,通常提供自己的 UI 來使用數據。 但是,內容提供程序主要旨在供其他應用使用,這些應用使用提供程序客戶端對象來訪問提供程序。 提供程序與提供程序客戶端共同提供一致的標准數據界面,該界面還可處理跨進程通信並保護數據訪問的安全性。

本主題介紹了以下基礎知識:

內容提供程序的工作方式。 用於從內容提供程序檢索數據的 API。 用於在內容提供程序中插入、更新或刪除數據的 API。 其他有助於使用提供程序的 API 功能。

Overview(概覽)
內容提供程序以一個或多個表(與在關系數據庫中找到的表類似)的形式將數據呈現給外部應用。 行表示提供程序收集的某種數據類型的實例,行中的每個列表示為實例收集的每條數據。

例如,Android 平台的內置提供程序之一是用戶字典,它會存儲用戶想要保存的非標准字詞的拼寫。 表 1 描述了數據在此提供程序表中的顯示情況:

這裡寫圖片描述表 1:用戶字典示例表格。
在表 1 中,每行表示可能無法在標准詞典中找到的字詞實例。 每列表示該字詞的某些數據,如該字詞首次出現時的區域設置。 列標題是存儲在提供程序中的列名稱。 要引用行的區域設置,需要引用其 locale 列。對於此提供程序,_ID 列充當由提供程序自動維護的“主鍵”列。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxzdHJvbmc+16KjuszhuamzzNDyzt7Q6L7f09DW97z8o6zSss7e0Oi9qyBfSUQg08PX98bk1ve8/LXEwdDD+7PGo6jI57n7tObU2tb3vPyjqaGjILWrysejrMjnufvE+tKqvavAtNfUzOG5qbPM0PK1xMr9vt3T6yBMaXN0VmlldyCw87aoo6zU8sbk1tDSu7j2wdDD+7PGsdjQ68rHIF9JRKGjPC9zdHJvbmc+PC9wPg0KPGgyIGlkPQ=="access-providers訪問提供程序">Access Providers(訪問提供程序)

應用從具有 ContentResolver 客戶端對象的內容提供程序訪問數據。 此對象具有調用提供程序對象(ContentProvider 的某個具體子類的實例)中同名方法的方法。 ContentResolver 方法可提供持續存儲的基本“CRUD”(創建、檢索、更新和刪除)功能。

客戶端應用進程中的 ContentResolver 對象和擁有提供程序的應用中的 ContentProvider 對象可自動處理跨進程通信。 ContentProvider 還可充當其數據存儲庫和表格形式的數據外部顯示之間的抽象層。

注:要訪問提供程序,您的應用通常需要在其清單文件中請求特定權限。

例如,要從用戶字典提供程序中獲取字詞及其區域設置的列表,則需調用 ContentResolver.query()。 query() 方法會調用用戶字典提供程序所定義的 ContentProvider.query() 方法。 以下代碼行顯示了 ContentResolver.query() 調用:

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows

表 2 顯示了 query(Uri,projection,selection,selectionArgs,sortOrder) 的參數如何匹配 SQL SELECT 語句:

這裡寫圖片描述
表 2:Query() 與 SQL 查詢對比。

Content URI

Content URI 是用於在提供程序中標識數據的 URI。Content URI 包括整個提供程序的符號名稱(其權限)和一個指向表的名稱(路徑)。 當您調用客戶端方法來訪問提供程序中的表時,該表的Content URI 將是其參數之一。

在前面的代碼行中,常量 CONTENT_URI 包含用戶字的“words”表的內容 URI。 ContentResolver 對象會分析出 URI 的授權,並通過將該授權與已知提供程序的系統表進行比較,來“解析”提供程序。 然後, ContentResolver 可以將查詢參數分派給正確的提供程序。

ContentProvider 使用Content URI 的路徑部分來選擇要訪問的表。 提供程序通常會為其公開的每個表顯示一條路徑。

在前面的代碼行中,“字詞”表的完整 URI 是:

content://user_dictionary/words

其中,user_dictionary 字符串是提供程序的授權, words 字符串是表的路徑。字符串 content://(架構)始終顯示,並將此標識為內容 URI。

許多提供程序都允許您通過將 ID 值追加到 URI 末尾來訪問表中的單個行。例如,要從用戶字典中檢索 _ID 為 4 的行,則可使用此內容 URI:

Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

在檢索到一組行後想要更新或刪除其中某一行時通常會用到 ID 值。

注:Uri 和 Uri.Builder 類 包含根據字符串構建格式規范的 URI 對象的便利方法。 ContentUris 包含一些可以將 ID 值輕松追加到 URI 後的方法。前面的代碼段就是使用 withAppendedId() 將 ID 追加到用戶字典內容 URI 後。

從提供程序檢索數據


本節將以用戶字典提供程序為例,介紹如何從提供程序中檢索數據。

為了明確進行說明,本節中的代碼段將在“UI 線程”上調用 ContentResolver.query()。但在實際代碼中,您應該在單獨線程上異步執行查詢。 執行此操作的方式之一是使用 CursorLoader 類,加載器中對此有更為詳細的介紹。 此外,前述代碼行只是代碼段;它們不會顯示整個應用。

要從提供程序中檢索數據,請按照以下基本步驟執行操作:

請求對提供程序的讀取訪問權限。 定義將查詢發送至提供程序的代碼。 請求讀取訪問權限

要從提供程序檢索數據,您的應需要具備對提供程序的“讀取訪問”權限。 您無法在運行時請求此權限;相反,您需要使用uses-permission元素和提供程序定義的准確權限名稱,在清單文件中指明您需要此權限。 在您的清單文件中指定此元素後,您將有效地為應用“請求”此權限。 用戶安裝您的應用時,會隱式授予允許此請求。

構建查詢

從提供程序中檢索數據的下一步是構建查詢。第一個代碼段定義某些用於訪問用戶字典提供程序的變量:

// A "projection" defines the columns that will be returned for each row
String[] mProjection =
{
    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    UserDictionary.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 = {""};

下一個代碼段以用戶字典提供程序為例,顯示了如何使用 ContentResolver.query()。 提供程序客戶端查詢與 SQL 查詢類似,並且包含一組要返回的列、一組選擇條件和排序順序。

查詢應該返回的列集被稱為投影(變量 mProjection)。

用於指定要檢索的行的表達式分割為選擇子句和選擇參數。 選擇子句是邏輯和布爾表達式、列名稱和值(變量 mSelectionClause)的組合。 如果您指定了可替換參數 ? 而非值,則查詢方法會從選擇參數數組(變量 mSelectionArgs)中檢索值。

在下一個代碼段中,如果用戶未輸入字詞,則選擇子句將設置為 null,而且查詢會返回提供程序中的所有字詞。 如果用戶輸入了字詞,選擇子句將設置為 UserDictionary.Words.WORD + ” = ?” 且選擇參數數組的第一個元素將設置為用戶輸入的字詞。

/*
 * This defines a one-element String array to contain the selection argument.
 */
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 words
    mSelectionClause = 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 table
    mProjection,                       // The columns to return for each row
    mSelectionClause                   // Either null, or the word the user entered
    mSelectionArgs,                    // Either empty, or the string the user entered
    mSortOrder);                       // 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 =  ORDER BY word ASC;

在此 SQL 語句中,會使用實際的列名稱而非協定類常量。

防止惡意輸入

如果內容提供程序管理的數據位於 SQL 數據庫中,將不受信任的外部數據包括在原始 SQL 語句中可能會導致 SQL 注入。
考慮此選擇子句:

// Constructs a selection clause by concatenating the user's input to the column name
String mSelectionClause =  "var = " + mUserInput;

如果您執行此操作,則會允許用戶將惡意 SQL 串連到 SQL 語句上。 例如,用戶可以為 mUserInput 輸入“nothing; DROP TABLE ;”,這會生成選擇子句 var = nothing; DROP TABLE ;。 由於選擇子句是作為 SQL 語句處理,因此這可能會導致提供程序擦除基礎 SQLite 數據庫中的所有表(除非提供程序設置為可捕獲 SQL 注入嘗試)。

要避免此問題,可使用一個用於將 ? 作為可替換參數的選擇子句以及一個單獨的選擇參數數組。 執行此操作時,用戶輸入直接受查詢約束,而不解釋為 SQL 語句的一部分。 由於用戶輸入未作為 SQL 處理,因此無法注入惡意 SQL。請使用此選擇子句,而不要使用串連來包括用戶輸入:

// Constructs a selection clause with a replaceable parameter
String mSelectionClause =  "var = ?";

按如下所示設置選擇參數數組:

// Defines an array to contain the selection arguments
String[] selectionArgs = {""};

按如下所示將值置於選擇參數數組中:

// Sets the selection argument to the user's input
selectionArgs[0] = mUserInput;

一個用於將 ? 用作可替換參數的選擇子句和一個選擇參數數組是指定選擇的首選方式,即使提供程序並未基於 SQL 數據庫。

顯示查詢結果

ContentResolver.query() 客戶端方法始終會返回符合以下條件的 Cursor:包含查詢的投影為匹配查詢選擇條件的行指定的列。 Cursor 對象為其包含的行和列提供隨機讀取訪問權限。 通過使用 Cursor 方法,您可以循環訪問結果中的行、確定每個列的數據類型、從列中獲取數據,並檢查結果的其他屬性。 某些 Cursor 實現會在提供程序的數據發生更改時自動更新對象和/或在 Cursor 更改時觸發觀察程序對象中的方法。

注:提供程序可能會根據發出查詢的對象的性質來限制對列的訪問。 例如,聯系人提供程序會限定只有同步適配器才能訪問某些列,因此不會將它們返回至 Activity 或服務。

如果沒有與選擇條件匹配的行,則提供程序會返回 Cursor.getCount() 為 0(空游標)的 Cursor 對象。

如果出現內部錯誤,查詢結果將取決於具體的提供程序。它可能會選擇返回 null,或拋出 Exception。

由於 Cursor 是行“列表”,因此顯示 Cursor 內容的良好方式是通過 SimpleCursorAdapter 將其與 ListView 關聯。

以下代碼段將延續上一代碼段的代碼。它會創建一個包含由查詢檢索到的 Cursor 的 SimpleCursorAdapter 對象,並將此對象設置為 ListView 的適配器:

// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] mWordListColumns =
{
    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[] mWordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
mCursorAdapter = 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
    mWordListColumns,                      // A string array of column names in the cursor
    mWordListItems,                        // An integer array of view IDs in the row layout
    0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
mWordList.setAdapter(mCursorAdapter);

注:要通過 Cursor 支持 ListView,游標必需包含名為 _ID 的列。 正因如此,前文顯示的查詢會為“Words”表檢索 _ID 列,即使 ListView 未顯示該列。 此限制也解釋了為什麼大多數提供程序的每個表都具有 _ID 列。

從查詢結果中獲取數據

您可以將查詢結果用於其他任務,而不是僅顯示它們。例如,您可以從用戶字典中檢索拼寫,然後在其他提供程序中查找它們。 要執行此操作,您需要在 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 實現包含多個用於從對象中檢索不同類型的數據的“獲取”方法。 例如,上一個代碼段使用 getString()。 它們還具有 getType() 方法,該方法會返回指示列的數據類型的值。

內容提供程序權限

提供程序的應用可以指定其他應用訪問提供程序的數據所必需的權限。 這些權限可確保用戶了解應用將嘗試訪問的數據。 根據提供程序的要求,其他應用會請求它們訪問提供程序所需的權限。 最終用戶會在安裝應用時看到所請求的權限。

如果提供程序的應用未指定任何權限,則其他應用將無權訪問提供程序的數據。 但是,無論指定權限為何,提供程序的應用中的組件始終具有完整的讀取和寫入訪問權限。

如前所述,用戶字典提供程序需要 android.permission.READ_USER_DICTIONARY 權限才能從中檢索數據。 提供程序具有用於插入、更新或刪除數據的單獨 android.permission.WRITE_USER_DICTIONARY 權限。

要獲取訪問提供程序所需的權限,應用將通過其清單文件中的 uses-permission元素來請求這些權限。Android 軟件包管理器安裝應用時,用戶必須批准該應用請求的所有權限。 如果用戶批准所有權限,軟件包管理器將繼續安裝;如果用戶未批准這些權限,軟件包管理器將中止安裝。

以下 元素會請求對用戶字典提供程序的讀取訪問權限:

    

插入、更新和刪除數據

與從提供程序檢索數據的方式相同,也可以通過提供程序客戶端和提供程序 ContentProvider 之間的交互來修改數據。 您通過傳遞到 ContentProvider 的對應方法的參數來調用 ContentResolver 方法。 提供程序和提供程序客戶端會自動處理安全性和跨進程通信。

插入數據
要將數據插入提供程序,可調用 ContentResolver.insert() 方法。此方法會在提供程序中插入新行並為該行返回內容 URI。 此代碼段顯示如何將新字詞插入用戶字典提供程序:
// 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 URI
    mNewValues                          // the values to insert
);

新行的數據會進入單個 ContentValues 對象中,該對象在形式上與單行游標類似。 此對象中的列不需要具有相同的數據類型,如果您不想指定值,則可以使用 ContentValues.putNull() 將列設置為 null

代碼段不會添加 _ID 列,因為系統會自動維護此列。 提供程序會向添加的每個行分配唯一的 _ID 值。 通常,提供程序會將此值用作表的主鍵。

newUri 中返回的內容 URI 會按照以下格式標識新添加的行:

content://user_dictionary/words/

id_value 是新行的 _ID 內容。 大多數提供程序都能自動檢測這種格式的內容 URI,然後在該特定行上執行請求的操作。

要從返回的 Uri 中獲取 _ID 的值,請調用 ContentUris.parseId()。

更新數據
要更新行,請按照執行插入的方式使用具有更新值的 ContentValues 對象,並按照執行查詢的方式使用選擇條件。 您使用的客戶端方法是 ContentResolver.update()。您只需將值添加至您要更新的列的ContentValues 對象。 如果您要清除列的內容,請將值設置為 null。

以下代碼段會將區域設置具有語言“en”的所有行的區域設置更改為 null。 返回值是已更新的行數:

// 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 URI
    mUpdateValues                       // the columns to update
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

您還應該在調用 ContentResolver.update() 時檢查用戶輸入。

刪除數據

刪除行與檢索行數據類似:為要刪除的行指定選擇條件,客戶端方法會返回已刪除的行數。 以下代碼段會刪除應用 ID 與“用戶”匹配的行。該方法會返回已刪除的行數。

// 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 URI
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

您還應該在調用 ContentResolver.delete() 時檢查用戶輸入。如需了解有關此內容的更多詳情,請閱讀防止惡意輸入部分。

Provider Data Types(提供程序數據類型)


內容提供程序可以提供多種不同的數據類型。用戶字典提供程序僅提供文本,但提供程序也能提供以下格式:

整型
長整型(長)
浮點型
長浮點型(雙倍)
提供程序經常使用的另一種數據類型是作為 64KB 字節的數組實施的二進制大型對象 (BLOB)。 您可以通過查看 Cursor 類“獲取”方法看到可用數據類型。

提供程序文檔中通常都列出了其每個列的數據類型。 用戶字典提供程序的數據類型列在其協定類 UserDictionary.Words 參考文檔中。 您也可以通過調用 Cursor.getType() 來確定數據類型。

提供程序還會維護其定義的每個內容 URI 的 MIME(多用途互聯網郵件擴展)數據類型信息。您可以使用 MIME 類型信息查明應用是否可以處理提供程序提供的數據,或根據 MIME 類型選擇處理類型。 在使用包含復雜數據結構或文件的提供程序時,通常需要 MIME 類型。 例如,聯系人提供程序中的 ContactsContract.Data 表會使用 MIME 類型來標記每行中存儲的聯系人數據類型。 要獲取與內容 URI 對應的 MIME 類型,請調用 ContentResolver.getType()。

MIME 類型引用部分介紹了標准和自定義 MIME 類型的語法。

提供程序訪問的替代形式


提供程序訪問的三種替代形式在應用開發過程中十分重要:

批量訪問:您可以通過 ContentProviderOperation 類中的方法創建一批訪問調用,然後通過 ContentResolver.applyBatch() 應用它們。
異步查詢:您應該在單獨線程中執行查詢。執行此操作的方式之一是使用 CursorLoader 對象。 加載器指南中的示例展示了如何執行此操作。
通過 Intent 訪問數據:盡管您無法直接向提供程序發送 Intent,但可以向提供程序的應用發送 Intent,後者通常具有修改提供程序數據的最佳配置。
下文將介紹批量訪問和修改。

1. 批量訪問

批量訪問提供程序適用於插入大量行,或通過同一方法調用在多個表中插入行,或者通常用於跨進程界限將一組操作作為事務處理(原子操作)執行。

要在“批量模式”下訪問提供程序, 您可以創建 ContentProviderOperation 對象數組,然後使用 ContentResolver.applyBatch() 將其分派給內容提供程序。 您需將內容提供程序的授權傳遞給此方法,而不是特定內容 URI。這樣可使數組中的每個 ContentProviderOperation 對象都能適用於其他表。 調用 ContentResolver.applyBatch() 會返回結果數組。

ContactsContract.RawContacts 協定類 的說明包括展示批量注入的代碼段。 聯系人管理器示例應用包含在其 ContactAdder.java 源文件中進行批量訪問的示例。

2. 通過 Intent 訪問數據

Intent 可以提供對內容提供程序的間接訪問。即使您的應用不具備訪問權限,您也可以通過以下方式允許用戶訪問提供程序中的數據:從具有權限的應用中獲取回結果 Intent,或者通過激活具有權限的應用,然後讓用戶在其中工作。

通過臨時權限獲取訪問權限
即使您沒有適當的訪問權限,也可以通過以下方式訪問內容提供程序中的數據:將 Intent 發送至具有權限的應用,然後接收回包含“URI”權限的結果 Intent。 這些是特定內容 URI 的權限,將持續至接收該權限的 Activity 結束。 具有永久權限的應用將通過在結果 Intent 中設置標志來授予臨時權限:

讀取權限: FLAG_GRANT_READ_URI_PERMISSION
寫入權限: FLAG_GRANT_WRITE_URI_PERMISSION

注:這些標志不會為其授權包含在內容 URI 中的提供程序 提供常規的讀取或寫入訪問權限。訪問權限僅適用於 URI 本身。

提供程序使用 provider 元素的 android:grantUriPermission 屬性以及 provider 元素的 grant-uri-permission 子元素在其清單文件中定義內容 URI 的 URI 權限。安全與權限指南中“URI 權限”部分更加詳細地說明了 URI 權限機制。

例如,即使您沒有 READ_CONTACTS 權限,也可以在聯系人提供程序中檢索聯系人的數據。您可能希望在向聯系人發送電子生日祝福的應用中執行此操作。 您更願意讓用戶控制應用所使用的聯系人,而不是請求 READ_CONTACTS,讓您能夠訪問用戶的所有聯系人及其信息。 要執行此操作,您需要使用以下進程:

您的應用會使用方法 startActivityForResult() 發送包含操作 ACTION_PICK 和“聯系人”MIME 類型
CONTENT_ITEM_TYPE 的 Intent 對象。

在選擇 Activity 中,用戶選擇要更新的聯系人。 發生此情況時,選擇 Activity 會調用setResult(resultcode, intent) 以設置用於返回至應用的 Intent。 Intent 包含用戶選擇的聯系人的內容 URI,以及“extras”標志 FLAG_GRANT_READ_URI_PERMISSION。這些標志會為您的應用授予讀取內容 URI 所指向的聯系人的數據的 URI 權限。然後,選擇 Activity 會調用 finish()以返回對應用的控制。

您的 Activity 會返回至前台,系統會調用您的 Activity 的 onActivityResult()
方法。此方法會收到“聯系人”應用中選擇 Activity 所創建的結果 Intent。

通過來自結果 Intent 的內容 URI,您可以讀取來自聯系人提供程序的聯系人數據,即使您未在清單文件中請求對該提供程序的永久讀取訪問權限。 您可以獲取聯系人的生日信息或其電子郵件地址,然後發送電子祝福。

使用其他應用
允許用戶修改您無權訪問的數據的簡單方法是激活具有權限的應用,讓用戶在其中執行工作。

例如,日歷應用接受 ACTION_INSERT Intent,這讓您可以激活應用的插入 UI。您可以在此 Intent(應用將使用該 Intent 來預先填充 UI)中傳遞“額外”數據,由於定期事件具有復雜的語法,因此將事件插入日歷提供程序的首選方式是激活具有 ACTION_INSERT 的日歷應用,然後讓用戶在其中插入事件。

Agreement type(協定類)


協定類定義幫助應用使用內容 URI、列名稱、 Intent 操作以及內容提供程序的其他功能的常量。 協定類未自動包含在提供程序中;提供程序的開發者需要定義它們,然後使其可用於其他開發者。 Android 平台中包含的許多提供程序都在軟件包 android.provider 中具有對應的協定類。

例如,用戶字典提供程序具有包含內容 URI 和列名稱常量的協定類 UserDictionary。 “字詞”表的內容 URI 在常量 UserDictionary.Words.CONTENT_URI 中定義。 UserDictionary.Words 類也包含列名稱常量,本指南的示例代碼段中就使用了該常量。 例如,查詢投影可以定義為:

String[] mProjection =
{
    UserDictionary.Words._ID,
    UserDictionary.Words.WORD,
    UserDictionary.Words.LOCALE
};

聯系人提供程序的 ContactsContract 也是一個協定類。 此類的參考文檔包括示例代碼段。其子類之一 ContactsContract.Intents.Insert 是包含 Intent 和 Intent 數據的協定類。

MIME type reference(MIME 類型引用)


內容提供程序可以返回標准 MIME 媒體類型和/或自定義 MIME 類型字符串。

MIME 類型具有格式

type/subtype

例如,眾所周知的 MIME 類型 text/html 具有 text 類型和 html 子類型。如果提供程序為 URI 返回此類型,則意味著使用該 URI 的查詢會返回包含 HTML 標記的文本。

自定義 MIME 類型字符串(也稱為“特定於供應商”的 MIME 類型)具有更加復雜的類型和子類型值。 類型值始終為

vnd.android.cursor.dir

(多行)或

vnd.android.cursor.item

(單行)。

子類型特定於提供程序。Android 內置提供程序通常具有簡單的子類型。 例如,當聯系人應用為電話號碼創建行時,它會在行中設置以下 MIME 類型:

vnd.android.cursor.item/phone_v2

請注意,子類型值只是 phone_v2。

其他提供程序開發者可能會根據提供程序的授權和表名稱創建自己的子類型模式。 例如,假設提供程序包含列車時刻表。 提供程序的授權是 com.example.trains,並包含表 Line1、Line2 和 Line3。在響應表 Line1 的內容 URI

content://com.example.trains/Line1

時,提供程序會返回 MIME 類型

vnd.android.cursor.dir/vnd.example.line1

在響應表 Line2 中第 5 行的內容 URI

content://com.example.trains/Line2/5

時,提供程序會返回 MIME 類型

vnd.android.cursor.item/vnd.example.line2

大多數內容提供程序都會為其使用的 MIME 類型定義協定類常量。例如,聯系人提供程序協定類 ContactsContract.RawContacts 會為單個原始聯系人行的 MIME 類型定義常量 CONTENT_ITEM_TYPE。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved