Displaying query results
ContentResolver.query()客戶端方法始終回傳一個Cursor,cursor包含符合查詢條件的行和查詢中projection指定的欄位,Cursor物件提供隨機讀取它包含的行和欄位。使用Cursor方法,你可以遍歷在查詢結果中的各行、決定每個欄位的資料型態、從欄位中取值出來及檢查查詢結果的其他特性。一些Cursor在provider的資料改變時會自動地更新物件,或在Cursor改變時觸發一些方法,或兩者都會。
如果沒有行符合selection條件,provider回傳Cursor物件而Cursor.getCount()為0(一個空的Cursor)。
如果內部錯誤發生,查詢結果會根據特別的provider,它可能會選擇回傳null,或是丟出Exception。
因為一個Cursor是一個行列表,顯示Cursor內容的一個好方式是透過SimpleCursorAdapter將Cursor連結到ListView。
以下片段接續了上面的片段,建立SimpleCursorAdapter物件包含藉由查詢描述擷取出的Cursor,接著設定此物件為ListView的Adapter。
// 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,cursor必須包含名稱為_ID的欄位。因為如此,查詢描述顯示擷取"words"資料表的_ID欄位,儘管如此,ListView不會顯示它。此限制也說明了為什麼大部分的provider對於每個資料表都有_ID欄位了。
Getting data from query results
比起簡單地顯示查詢結果,妳可能會使用它們完成其他任務。例如,你可以從user dictionary擷取拼法,接著在其他provider中尋找它們。要完成這項功能,你要遍歷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實現包含許多"get"方法擷取來自物件不同資料型態的資料。例如,前面的片段使用getString()。它們也有getType()方法,回傳欄位的資料型態。
Content Provider Permissions
一個provider的應用程式可以指定權限讓其他應用程式要存取provider資料必須有該權限,這些權限確保了使用者知道應用程式將會試著存取什麼樣的資料。根據provider的需求,其他應用程式為了存取provider資料,請求它們需要的權限。終端使用者在安裝應用程式時會看到請求的權限。
取得存取provider所需的權限,應用程式在它的manifest文件中用<uses-permission>標籤請求。
以下為請求讀取User Dictionary Provider權限
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Inserting, Updating, and Deleting Data
如同我們從provider擷取資料出來的方法,你也可以使用provider客戶端與provider的ContentProvider間的互動修改資料。你以參數呼叫ContentResolver方法,參數會被傳遞到相對應的ContentProvider方法。provider和provider客戶端會自動地處理安全性與處理程序間的溝通。
Inserting data
將資料插入provider,呼叫ContentResolver.insert()方法。此方法插入新的行到provider並回傳該行的content Uri。以下片段插入新的單字到User Dictionary 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(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 );新行的資料進入一個單一ContentValue物件,它格式有點類似包含一行的cursor。此物件中的欄位不需要有相同的資料型態,且如果不想要指定數值,你可以使用ContentValue.putNull()設定欄位為null。
不需要加入_ID欄位,因為此欄位是自動維護的。provider會分配給被加入的每一行一個_ID唯一值。provider通常使用此值作為資料表的主鍵。
回傳到mNewValues的content Uri用來辨識新加入的行,如以下格式
content://user_dictionary/words/<id_value><id_value>為新行的_ID值,大部分的provider能自動地偵測此格式的content Uri,然後執行在特定行上的操作。
從回傳的Uri中取得_ID值,呼叫ContentUris,parseId()。
Updating data
更新一個row,你可以使用ContentValues物件與更新資料一起,就如同在插入資料時所做的一樣,selection條件也和在query時一樣。在客戶端使用的方法是ContentResolver.update(),只需要針對欲更新欄位,加數值到ContentValues物件。如果想要清除欄位的內容,把數值設為null。
以下片段將所有的row它的locale為"en"改變為null,回傳值為有改變的row個數。
// 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()時,也應該將使用者輸入做消毒,就像在Protecting against malicious input。
Deleting data
刪除rows類似於擷取row資料,針對想刪除的row指定selection條件,且客戶端方法回傳被刪除的row的號碼。以下片段為刪除appid符合"user"的row,方法回傳被刪除的row的號碼。
// 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()時,也應該將使用者輸入做消毒,就像在Protecting against malicious input。
Provider Data Types
Content providers提供許多不同的資料型態。User Dictionary Provider只有文字型態,但provider可以提供以下型態
- integer
- long integer (long)
- floating point
- long floating point (double)
provider常使用的它資料型態是Binary Large OBject (BLOB)被實作為64KB的byte array。可用的資料型態可到Cursor類別看看"get"方法。
一個provider中,每個欄位的資料型態通常被列在它的文件中。
provider也為每個content URI維護MIME型態資訊。
Alternative Forms of Provider Access
應用程式發展過程中,以下三種provider存取的替代形式是重要的:
- Batch access - 你可以在ContentProviderOperation類別中建立呼叫存取方法的批次處理,接著運用在ContentResolver.applyBatch()。
- Asynchronous queries - 應該獨立的執行緒中執行查詢,一種達成方式是使用CursorLoader物件。
- Data access vis intents - 雖然你無法直接地傳送一個intent,你可以傳送intent到provider的應用程式,應用程式有最佳的條件去修改provider資料。
沒有留言:
張貼留言