Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android Content Provider詳解二

android Content Provider詳解二

編輯:關於android開發

從Provider取得data

本節講述了如何從provider取得數據,使用用戶詞典作為例子.

為了清析易懂,本節中調用ContentResolver.query()的代碼片斷置於"UI 線程"中.但是,在實際代碼中,你應該在另一個線程執行查詢動作,這樣做的一種方法是使用CursorLoader 類.而,那幾行示例代碼僅是片斷,它們不能展示一個完整的應用.

要從provider取得data,須依如下步驟:

1 請求provider的讀權限.

2 定義發送請求到provider的代碼.

 

請求讀權限

要從一個provider中獲取數據,你的應用需要對目標provider具有"讀權限".你不能在運行時請求此權限,而只能在manifest文件中使用 <uses-permission> 元素指定你的權限需求.當你在manifest中指定此元素時,你實際上就是在為你的應用請求這個權限.當用戶安裝你的應用時,就表示同意了這個權限請求.

要找到你使用的provider讀權限的所對應的准確名字,以及其它用於provider的權限的名字,請浏覽provider的文檔.

關於操作provider的權限的角色的更多信息,請見Content Provider權限一節.

用戶詞典Provider在它的manifest 中定義了android.permission.READ_USER_DICTIONARY 權限,所以一個想讀取它內容的應用必須請求此權限.

 

構建請求

獲取數據的下一步是構建一個請求(query).這裡的第一個代碼片段定義了一些用於操作用戶詞典Provider的變量:


// "projection" 定義了要返回的各列們
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
};

// 定義一個包含"select"條款的字符串
String mSelectionClause = null;

// 初始化一個包含"select"參數的字符串
String[] mSelectionArgs = {""};

下一個代碼片段演示了如何使用ContentResolver.query(),將用戶詞典Provider作為一個例子.一個provider客戶端查詢極像一個SQL查詢,它包含了要返回的一坨column們,一堆篩選條件,和一個排序方式.

查詢返回的column集合被稱作projection (變量 mProjection).

指定返回的列的語句被分解為選擇條款和選擇參數兩部分.選擇條款是邏輯和布爾表達式,列名以及值的組合體(變量mSelection).如果你在其中指定了使用 ? 來代表一個值,查詢方法就會從選擇參數部分取得這個值(變量mSelectionArgs).

在下一個代碼片段中,如果用戶沒有輸入單詞,選擇條款就被設為null,並且查詢會反回所provider中所有的單詞.如果用戶輸入了單詞,選擇條款就被設置為UserDictionary.Words.Word + " = ?" 並且選擇參數(數組)的第一項被設置為用戶輸入的單詞.

/*
 * 定義一個一維的字符串數組來容納選擇參數們
 */
String[] mSelectionArgs = {""};

// 從界面中獲取一個單詞
mSearchString = mSearchWord.getText().toString();

// 記住要在此插插入代碼檢查不合法的或惡意的輸入.

// 如果單詞是空的,則獲取所有數據
if (TextUtils.isEmpty(mSearchString)) {
    // 設置選擇條款為null就會返回所有單詞
    mSelectionClause = null;
    mSelectionArgs[0] = "";

} else {
    // 構造一個匹配用戶輸入的單詞的選擇條款
    mSelectionClause = " = ?";

    // 將用戶輸入的單詞置於選擇參數中
    mSelectionArgs[0] = mSearchString;

}

// 執行查詢並返回游標對象
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

// 有些provider在出錯時返回null,有拋出異常
if (null == mCursor) {
    /*
     * 在此插入代碼處理錯誤.記住不要使用游標! 你可能要調用
     * android.util.Log.e()把錯誤記錄的日志
     *
     */
// 如果游標是空的,找不到匹配的provider
} else if (mCursor.getCount() < 1) {

    /*
     * 在此插入代碼來通知用戶,查找不成功.這也不能完全算是個錯誤.你可能想為用戶提供插入一個新行或重新輸入查詢單詞的選項
     */

} else {
    // 在此插入代碼,利用返回的結果做想做的事

}

查詢與下面的SQL語句等價:

SELECT _ID, word, frequency, locale FROM words WHERE word = <userinput> ORDER BY word ASC;

在此SQL 語句中,以實際的列名代替了內置的類別常量.

 

防止惡意輸入

如果被content provider管理的數據是一個SQL 數據庫,在原始的SQL語句中包含不可信的數據會導致SQL注入.

思考以下選擇條款:

// 通過連接用戶輸入到列名來構造一個選擇條款
String mSelectionClause =  "var = " + mUserInput;

如果你這樣做,你就允許用戶連接惡意的SQL語句到你的SQL語句中.例如,用戶可以輸入"nothing; DROP TABLE *;" ,這將在選擇條款中變為 var = nothing; DROP TABLE *;..既然選擇條款被作為SQL語句,這就可能導致provider刪除SQLite數據庫中的所有的表(除非provider被設置成捕獲SQL injection 陰謀).

要避免此問題,應使用一個運用?作為可替換參數的選擇條款和一個作為選擇參數的數組.當你這樣做時,用戶輸入被直接綁定到查詢而不是被解釋為SQL語句的一部分.因為它不被認為是SQL,於是用戶輸入就不能注入惡意SQL.使用以下選擇條款來代替連接用戶輸入的那個:

// 構造一個帶有占位符的選擇條款
String mSelectionClause =  "var = ?";

像這樣建立起選擇參數數組:

// 定義一個數組來容納選擇參數
String[] selectionArgs = {""};

像這樣把一個值置入選擇參數數組中:

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

一個使用?作為占位符的選擇條款+一個選擇參數數組是指定一個選擇器的最佳方式,即使provider不是基於SQL數據庫的.

顯示查詢結果

客戶端方法ContentResolver.query() 總是返回一個包含所查詢的列們的Cursor .一個Cursor 對象提供了隨機的讀取它所包含的行和列的能力.使用Cursor 的方法們,你可以迭代結果中的行,決定每列的數據類型,從列獲得數據,以及檢測結果的其它屬性.一些Cursor 的實現會在provider的數據改變時自動更新,或在Cursor 改變時觸發監聽者的方法,或者兩者都支持.

注: 一個provider可能跟據構建查詢的對象的性質限制對某些列的操作.例如,聯系人Provider會禁止同步適配器操作某些列,所以它不會把它們返回給一個activity或service.

如果沒有符合選擇條件的行,provider返回一個Cursor 對象,其Cursor.getCount() 為0 (一個空cursor).

如果一個內部錯誤發生,查詢結果會因provider的不同而不同.它可能返回null,也可能拋出一個Exception.

既然一個Cursor 是行組成的"列表",那麼一個和顯示Cursor 內容的好方法就是把它鏈接到一個ListView 上,通過SimpleCursorAdapter.

下面的代碼片段是銜接前面的代碼來的.它創建一個SimpleCursorAdapter 對象,包含有查詢返回的Cursor ,然後設置這個對象為ListView的適配器.

// 定義要從Cursor取出的並要加載到view中的列們
String[] mWordListColumns =
{
    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
};

// 定義一個View ID組成的列表,它們將接收每行的Cursor列的值
int[] mWordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(),               // The application's Context object
    R.layout.wordlistrow,                  // ListView的一行的layout
    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)

// 將適配器設置給ListView
mWordList.setAdapter(mCursorAdapter);

注:要使Cursor支持ListView,cursor必須包含一個叫做_ID的列,因此,上面所示的查詢從"單詞"表中取出了_ID ,當然ListView 可以不顯示它.這條限制同時也解釋了為毛大多數provider在它們的表中都具有一個_ID 列.

從查詢結果中獲取數據

 

你可以使用查詢結果做更多是事情,而不是僅簡單地顯示它們.比如,你可以從用戶詞典中獲取拼法然後在其它provider中查找它們.要這樣做,你需在Cursor中迭代所有的行.


// 獲取叫做"word"的列的序號
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);

/*
 * 僅在cursor有效時執行下面語句.如果發生內部錯誤,用戶詞典Provider返回null.
 * 其它provider可能拋出一個異常而不是返回null.
 */

if (mCursor != null) {
    /*
     * 移到cursor中的下一行.在第一次移動之前,
     * "行指針" 為-1,並且,如果你想獲取那個位置的數據,你將得到一個異常
     */
    while (mCursor.moveToNext()) {

        // 從列中獲取值.
        newWord = mCursor.getString(index);

        // 在此插入代碼處理獲取到的單詞

        ...

        // 循環結束
    }
} else {

    // 如果cursor為null或前面拋出了異常,在處插入代碼報告錯誤.
}

Cursor 的實現包含了多個"get" 方法,用於從對象中獲取不同類型的數據.例如,前面的代碼片段使用getString().它們也具有一個getType() 方法,用它可以返回的值代表了數據的類型.

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