5、訪問Content Provider
這裡首先學習如何使用Content provider(包括系統提供的,比如建立一個簡訊收發系統)。
Content Providers的使用者都不可能直接存取到Content Provider執行個體,只能通過ContentResolver在中間代理。用戶端直接使用Content Resolver對象進行互動,Content Resolvers 方法提供了基本的持續儲存資料的函數CRUD(create、retrieve、update和delete)。用戶端如何擷取ContentResolver的執行個體,可通過如下Activity的成員方法擷取。不過當你訪問provider時,你的應用程式應在mainfest檔案請求相應的許可權。
ContentResolver cr= getContext().getContentResolver();
ContentResolver提供了5個基本的對Content Provider操作的方法,後面將會深入介紹。當你建立自己的Content Provider,也就是繼承了ContentProvider類時,意味著你也同時實現這5個方法(在後續章節會介紹到)。
1、final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)//Query the given URI, returning a Cursor over the result set.final Cursor managedQuery(Uri uri,String[] projection,String selection,String[]selectionArgs,String sortOrder)2、final Uri insert(Uri url, ContentValues values)//Inserts a row into a table at the given URL.3、final int update(Uri uri, ContentValues values, String where, String[] selectionArgs)//Update row(s) in a content URI.4、final int delete(Uri url, String where, String[] selectionArgs)//Deletes row(s) specified by a content URI.5、final String getType(Uri url)//Return the MIME type of the given content URL.
上面面提供的兩種查詢方法,區別在於方法managedQuery()的Cursor的生命週期由activity管理。管理的好處:activity幫你處理細節,如activity暫停時卸載,activity重新開始時重新請求。對於未被activity管理的Cursor可通過此函數讓activit管理:
Activity.startManagingCursor(Cursor cursor)
6、從provider查詢資料
查詢主要下面兩步(以User Dictinary為例)
(1)請求讀取許可權。在mainfest檔案中配置android.permission.READ_USER_DICTIONARY
(2)自訂查詢的代碼。這裡主要有兩種處理方式:一種是顯示查詢的結果,另外一種是擷取查詢的資料。
另外當你精確尋找某一個記錄時,也就意味著你的裡Uri需要包含其_ID值,可以通過以下兩種方法追加,如下:
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");
6.1 首先需要構造一個查詢
// 指定要返回的每行的欄位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};// 定義查詢子句的字串(類似Sql的where子句,但無where關鍵字)String mSelectionClause = null;// 初始化數組,其包含查詢子句的參數值(即選擇字句預留位置“?”值)String[] mSelectionArgs = {""};/* * This defines a one-element String array to contain the selection argument. */String[] mSelectionArgs = {""};// 擷取使用者輸入mSearchString = mSearchWord.getText().toString();//記得檢查使用者輸入有效性// 為空白就用null,代表查詢所有的資料if (TextUtils.isEmpty(mSearchString)) { mSelectionClause = null; mSelectionArgs[0] = "";} else { // 根據使用者輸入構造查詢子句 mSelectionClause = UserDictionary.Words.WORD + " = ?"; // 使用者輸入的字元 mSelectionArgs[0] = mSearchString;}// 通過ContextResolver物件查詢,返回Cursor對象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// 對查詢的cursor處理(空或者異常等)if (null == mCursor) { /* * 處理錯誤,此時mCursor不能使用 * call android.util.Log.e() to log this error. * */// If the Cursor is empty, the provider found no matches} else if (mCursor.getCount() < 1) { /* * 查詢失敗,通知使用者無效查詢子句 * 也有可能是你想推薦給使用者的輸入 */} else { // 處理正確的查詢結果}
註:使用預留位置“?”,可以有效阻止惡意的SQL語句,也就是為什麼間接使用mSelectionClause和mSelectionArgs來構造where語句的原因。
6.2.1顯示查詢結果
查詢返回的是Cursor,此物件類型是列表行,因此顯示Cursor內容最好憑藉SimpleCursorAdapter填充器將之連結到ListView上。
(1)如果不匹配selection,provider返回空的Cursor對象,此時Cursor.getCount()為0
(2)如果發生內部錯誤,查詢結果取決於provider(可能返回null,也可能為Exception)
// 定義從Cursor返回的欄位列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,也就是int to[]int[] mWordListItems = { R.id.dictWord, R.id.locale};// Creates a new SimpleCursorAdaptermCursorAdapter = 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 ListViewmWordList.setAdapter(mCursorAdapter);
6.2.2 從查詢結果中擷取資料
// 擷取該欄位的索引int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);/* * 如果cursor有效時才執行,可能返回null、內部錯誤或者Exception */if (mCursor != null) { /* * 在執行while語句即移動前,應首先在這裡將cursor移動到下一行 *如果嘗試直接查詢資料,將會獲得異常, 因為"row pointer" 是 -1 */ while (mCursor.moveToNext()) { // 從欄位索引中擷取相應的值 newWord = mCursor.getString(index); // 處理查詢的值. ... // end of while loop }} else { //如果cursor為null或者provider扔出exception,處理}
6.3插入資料:
使用ContentResolver.insert()方法,其插入新行到provider,同時返回新加行的URI
// 定義一個新的Uri對象,其接收插入的結果Uri mNewUri;...// 定義一個對象用來暫存需插入的值ContentValues mNewValues = new ContentValues();/* * 設定插入的每一個欄位的值,參數:欄位名,欄位值 */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);
註:(1)由於SQLite資料庫特性,這裡不需要放置與欄位相同類型的資料,甚至你可以不需要指定值,使用ContentValues.putNull()。
(2)主鍵自動成長(autoincrement),provider分配給每一行獨一的_ID,用作主鍵。
(3)返回的新的URI如下,<id_value>就是_ID值,可利用該id對此行執行相應操作。擷取此_ID值,通過ContentUris.parseId();
content://user_dictionary/words/<id_value>
6.4更新資料
用戶端使用ContentResolver.update()更新資料,返回更新的行數。利用ContentValues對象暫存資料,其中只需要儲存想改變的欄位。若想清除,直接設定值為null就可以了。
// 定義ContentValues對象,暫存需更新的資料ContentValues mUpdateValues = new ContentValues();// 定義想更新的行的查詢子句String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?";String[] mSelectionArgs = {"en_%"};// 定義一變數,儲存被更新的行的總數int mRowsUpdated = 0;.../* * 需更新值得儲存 */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);
6.5刪除資料
類似於查詢,指定想刪除行的條件,同時將返回刪除的行數。執行刪除操作時,應當對使用者的輸入採取一定錯數,以防惡意輸入。
// 定義刪除條件String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";String[] mSelectionArgs = {"user"};// 定義變數,儲存刪除的行數int mRowsDeleted = 0;...// 刪除mRowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI mSelectionClause // the column to select on mSelectionArgs // the value to compare to);