2015年7月8日 星期三

[擒猿基本功] Android 利用 Content Provider 隔山打牛

安卓大陸裡處處是陷阱,必須集合眾人的力量才能擊敗邪惡 Monkey,而 Content Provider 就是一套能夠借力使力,隔山打牛的強大心法,不可不練,非練不可。

翻開擒猿寶典,Content Provider 提供了一個中央控管的資料庫,可以讓其他的 app 可以購過 API 方便存取本身的資料,以下介紹 Content Provider 的原理與實作:

Content Provider 將資料以 Table 的方式儲存,其中一個 row 表示一筆資料,每個  row 會有多個 column,Table 內容如下:
wordapp_idfrequencylocale_ID
mapreduceuser1100en_US1
precompileruser14200fr_FR2
appletuser2225fr_CA3
constuser1255pt_BR4
intuser5100en_UK5

鏡頭一轉,來到安卓大陸的戰場上,火爆猿王肆虐,打得眾人潰不成軍,一位武功強大的老前輩挺身而出,利用獅吼功向眾人發出訊息:幹你娘!大家快來用我的 Content Provider 打王!

這時候身為擒猿手的你,該怎麼提取前輩的內力,借力對邪惡猿王打出致命一擊呢?噎,這時候就要透過 ContentResolver 這個物件,透過他就可以對前輩的 Content Provider 進行 CRUD 操作 (C: Create、R: Retrieve、U: Update、D: Delete)。

Query Data from Content Provider

欲取得 Content Provider 分享資料的 app 必須在 manifest 中宣告 permission,而欲存取資料的 app 則需在 manifest 中去 use 這個 permission。在程式中可以利用以下寫法得到指定 Content Provider 的 Table 中的資料:
// Queries the user dictionary and returns results
Cursor mCursor = getContentResolver().query(
    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
  • CONTENT_URI 以 URI 的形式直接指向 Content Provider 的 Table,形如 content://user_dictionary/words,其中 user_diction 為 Content Provider authority,而 words 為 Table name。
  • mProjecttion 定義取出的資料要有那些欄位,寫法如下:
  • String[] mProjection = {
        "_ID",
        "word",
        "locale"
    };
    • mSelectionClause 可定義的篩選資料的條件,寫法如下:
      String mSelectionClause =  "frequency = 200";
      但這樣的寫法非常危險,因為他直接對應到 SQL 語言,譬如 user 可能輸入 "nothing; DROP TABLE *;" ,導致 Table 被清空。
    • 更好的寫法是 mSelectionArgs 與 mSelectionClause 搭配使用,能避免上述的惡意破壞情形,寫法如下,注意那個問號很玄:
    • // Constructs a selection clause with a replaceable parameter
      String mSelectionClause = "frequency = ?";
      // Defines an array to contain the selection arguments
      String[] selectionArgs = {""};
      // Sets the selection argument to the user's input
      selectionArgs[0] = mUserInput;
    • 最後,可以利用 mSortOrder 去控制取回資料的排序方式,寫法如下:
    • mSortOrder = "ORDER BY _ID";

    Getting data from query results

    使用上述的 ContentResolver.query() 會傳回一個 Cursor 元件,以下為從 Cursor 中取得我們要的資料的方法:
    // Determine the column index of the column named "word"
    int index = mCursor.getColumnIndex("world");
    if (mCursor != null) {
        while (mCursor.moveToNext()) {
            // Gets the value from the column.
            newWord = mCursor.getString(index);
            Log.d("Lotus", newWord);
        }
    }
    
    此外可以直接將 Cursor 與 ListView 做連結,如以下寫法:
    // 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
        new Steing[] {"word" "locale"}, // A string array of column names in the cursor
        new int[] { R.id.dictWord, R.id.locale}, // view IDs in the row layout
        0);                             // Flags (usually none are needed)
    
    // Sets the adapter for the ListView
    mListview.setAdapter(mCursorAdapter);

    Inserting data

    利用 ContentResolver.insert() 可達成插入一筆新資料至 Content Provider 的動作,寫法如下 :
    // 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("app_id", "example.user");
    mNewValues.put("local", "en_US");
    mNewValues.put("word", "insert");
    mNewValues.put("frequency", "100");
    
    mNewUri = getContentResolver().insert(
        CONTENT_URI,   // the user dictionary content URI
        mNewValues     // the values to insert
    );

    Updating data

    利用 ContentResolver.update() 可達成對 Content Provider 的資料做更新動作,寫法如下 :
    // Defines an object to contain the updated values
    ContentValues mUpdateValues = new ContentValues();
    
    // Defines selection criteria for the rows you want to update
    String mSelectionClause = "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("local");
    
    mRowsUpdated = getContentResolver().update(
        CONTENT_URI,      // the user dictionary content URI
        mUpdateValues     // the columns to update
        mSelectionClause  // the column to select on
        mSelectionArgs    // the value to compare to
    );


    Deleting data

    利用 ContentResolver.delete() 可達成對 Content Provider 的資料做刪除動作,寫法如下 :
    // Defines selection criteria for the rows you want to delete
    String mSelectionClause = "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(
        CONTENT_URI,      // the user dictionary content URI
        mSelectionClause  // the column to select on
        mSelectionArgs    // the value to compare to
    );

    Reference: http://developer.android.com/guide/topics/providers/content-provider-basics.html

    1 則留言:

    1. Your Affiliate Profit Machine is ready -

      And getting it set up is as simple as 1-2-3!

      Here are the steps to make it work...

      STEP 1. Input into the system which affiliate products the system will push
      STEP 2. Add PUSH BUTTON traffic (this LITERALLY takes 2 minutes)
      STEP 3. See how the affiliate system explode your list and sell your affiliate products on it's own!

      So, do you want to start making money??

      Check it out here

      回覆刪除