解讀Content Provider之二 – Android移動開發技術文章_手機開發 Android移動開發教學課程

修改數據
   可以通過如下方法修改被content provider保存的數據:
   1.添加新的記錄;
   2.為已經存在的數據添加新值;
   3.批量更新已經存在的記錄;
   4.刪除記錄
   所有的數據修改操作都可以通過ContentResolver的方法來完成。一些content provider在修改數據的時候要求擁有比讀取數據擁有更多的權限。如果沒有修改content provider數據的權限,那麼ContentResolver的方法就會失效。
  添加記錄
   為瞭添加新的記錄到content provider中,首先在ContentValues對象中設置鍵-值對,設置的鍵和content provider中的列名對應,並且值的內容就是我們想為新記錄設置值。然後,通過調用ContentResolver.insert()方法,並且將要處理的content provider和設置好的ContentValues鍵-值對作為參數傳遞給這個方法。這個方法將會返回新紀錄的完整的URI——也就是,要操作的provider的URI加上為新記錄追加的ID號。然後,你可以用這個完整的URI來進行查詢操作,獲得一個指向這個新記錄的Cursor,並且利用這個得到的Cursor進行後續的修改記錄的操作。如下例:
    import android.provider.Contacts.People; 
import android.content.ContentResolver; 
import android.content.ContentValues;  
 
 
ContentValues values = new ContentValues(); 
 
 
// Add Abraham Lincoln to contacts and make him a favorite.  
values.put(People.NAME, "Abraham Lincoln"); 
// 1 = the new contact is added to favorites  
// 0 = the new contact is not added to favorites  
values.put(People.STARRED, 1); 
 
 
Uri uri = getContentResolver().insert(People.CONTENT_URI, values); 
    import android.provider.Contacts.People;
 import android.content.ContentResolver;
 import android.content.ContentValues;

 ContentValues values = new ContentValues();

 // Add Abraham Lincoln to contacts and make him a favorite.
 values.put(People.NAME, "Abraham Lincoln");
 // 1 = the new contact is added to favorites
 // 0 = the new contact is not added to favorites
 values.put(People.STARRED, 1);

 Uri uri = getContentResolver().insert(People.CONTENT_URI, values);  添加新的值
    如果一條記錄已經存在,你可以添加新的信息到其中或者是修改已經存在的信息。例如:上面例子的下一步就是添加新的聯系信息——例如一個電話號碼或者是一個IM帳號再或者是郵箱的地址——到新的條目中。
    向Contacts數據庫中添加新紀錄的最好的方法就是追加表名到存儲新紀錄的URI後,然後用修改過的URI來添加新的數據值。每一個Contacts表都會暴露出一個名字作為CONTENT_DIRECTORY常量。接下來的代碼是接著上面的例子,要為新創建的記錄添加一個電話號碼和一個郵箱地址。
Uri phoneUri = null; 
Uri emailUri = null; 
 
 
// Add a phone number for Abraham Lincoln.  Begin with the URI for  
// the new record just returned by insert(); it ends with the _ID  
// of the new record, so we don't have to add the ID ourselves.  
// Then append the designation for the phone table to this URI,  
// and use the resulting URI to insert the phone number.  
phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY); 
 
 
values.clear(); 
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE); 
values.put(People.Phones.NUMBER, "1233214567"); 
getContentResolver().insert(phoneUri, values); 
 
 
// Now add an email address in the same way.  
emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY); 
 
 
values.clear(); 
// ContactMethods.KIND is used to distinguish different kinds of  
// contact methods, such as email, IM, etc.   
values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL); 
values.put(People.ContactMethods.DATA, "test@example.com"); 
values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME); 
getContentResolver().insert(emailUri, values); 
 Uri phoneUri = null;
 Uri emailUri = null;

 // Add a phone number for Abraham Lincoln.  Begin with the URI for
 // the new record just returned by insert(); it ends with the _ID
 // of the new record, so we don't have to add the ID ourselves.
 // Then append the designation for the phone table to this URI,
 // and use the resulting URI to insert the phone number.
 phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);

 values.clear();
 values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
 values.put(People.Phones.NUMBER, "1233214567");
 getContentResolver().insert(phoneUri, values);

 // Now add an email address in the same way.
 emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);

 values.clear();
 // ContactMethods.KIND is used to distinguish different kinds of
 // contact methods, such as email, IM, etc.
 values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);
 values.put(People.ContactMethods.DATA, "test@example.com");
 values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);
 getContentResolver().insert(emailUri, values);

    你可以通過調用ContentValues.put()方法通過一個byte類型的數組將少量的二進制數據加入到表中。例如:這種方法對小的圖標類型的圖片和短小的音頻文件是十分有效的。然而,如果你有比較大的二進制數據要添加,例如一個圖片文件和一首完整的歌曲,為這個要添加的數據這是一個content:URI,然後調用ContentResolver.openOutputStream()方法,並且這個文件的URI作為這個方法的參數。(這將使得content provider使用文件來存儲這些數據,並且將文件文件的路徑記錄在這條記錄的一個隱含的域中)
    在這方面,MediaStore content provider獨立於圖片、音頻和視頻文件的主要的provider,這就提供瞭一個便利:我們可以通過query()和managedQuery()使用一樣的URI來獲取二進制數據(例如:捕獲到的圖片)的信息,我們可以通過openInputStream()方法和剛剛獲得的二進制數據的信息來獲取這些二進制數據,下面的代碼就顯示瞭這個便利之處:
import android.provider.MediaStore.Images.Media; 
import android.content.ContentValues; 
import java.io.OutputStream; 
 
 
// Save the name and description of an image in a ContentValues map.    
ContentValues values = new ContentValues(3); 
values.put(Media.DISPLAY_NAME, "road_trip_1"); 
values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles"); 
values.put(Media.MIME_TYPE, "image/jpeg"); 
 
 
// Add a new record without the bitmap, but with the values just set.  
// insert() returns the URI of the new record.  
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values); 
 
 
// Now get a handle to the file for that record, and save the data into it.  
// Here, sourceBitmap is a Bitmap object representing the file to save to the database.  
try { 
    OutputStream outStream = getContentResolver().openOutputStream(uri); 
    sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream); 
    outStream.close(); 
} catch (Exception e) { 
    Log.e(TAG, "exception while writing image", e); 
}     
 import android.provider.MediaStore.Images.Media;
 import android.content.ContentValues;
 import java.io.OutputStream;

 // Save the name and description of an image in a ContentValues map. 
 ContentValues values = new ContentValues(3);
 values.put(Media.DISPLAY_NAME, "road_trip_1");
 values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
 values.put(Media.MIME_TYPE, "image/jpeg");

 // Add a new record without the bitmap, but with the values just set.
 // insert() returns the URI of the new record.
 Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);

 // Now get a handle to the file for that record, and save the data into it.
 // Here, sourceBitmap is a Bitmap object representing the file to save to the database.
 try {
     OutputStream outStream = getContentResolver().openOutputStream(uri);
     sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
     outStream.close();
 } catch (Exception e) {
     Log.e(TAG, "exception while writing image", e);
 }    批量更新數據
    要批量更新一組記錄(例如:要將所有域的“New York”都改變為“NY”)。調用ContentResolver.update()方法,將要修改的列和數值作為參數。
刪除一條記錄
    要刪除單個記錄,使用特殊行的URI作為參數,調用ContentResolver.delete()進行刪除
    要刪除多個行(多條記錄),使用要刪除的記錄的數據類型的URI和一條定義哪個行要被刪除的SQL WHERE語句作為參數調用ContentResolver.delete()方法(例如:android.provider.Contacts.People.CONTENT_URI)。(註意:如果你要刪除一般的類型,那就要確保包含瞭一條正確的WHERE語句,否則你就會刪除比自己設想的要刪除的記錄更多的記錄)。
創建一個Content Provider
    要創建一個content provider,你必須:
    1.設置一個存儲數據的系統。大多數的content providers都會使用Android系統的文件存儲方法或者是SQLite數據庫來存儲這些數據,但是你可以以任何方式存儲你自己的數據。Android提供瞭SQLiteOpenHelper類來幫助你創建數據庫和SQLiteDatabase來管理數據庫。
    2.擴展ContentProvider類提供存儲數據的方法
    3.在你的應用程序的ANndroidManifest.xml文件中聲明自己的content provider。
擴展ContentProvider類
    你可以定義一個ContentProvider子類將自己的數據暴露給那些通過ContentResolver和Cursor對象使用這些數據的對象。原則上,這也意味著要實現6個定義在ContentProvider類中的抽象方法:
    query()
    insert()
    update()
    delete()
    getType()
    onCreate()
    query()方法必須返回一個Cursor對象,通過它可以操作請求的數據。Cursor是一個接口,但是Android提供瞭一些定義好的Cursor對象,可以供直接使用。例如:SQLiteCursor能夠操作那些存儲在SQLite數據庫中數據。你可以通過SQLiteDatabase類的query()方法獲得Cursor對象。這裡有一些其他的Cursor實現——例如:MatrixCursor——對於那些不存儲再數據庫中的數據。
    因為這些ContentProvider方法可以被處於多個進程和多個線程中的ContentResolver對象調用,因此他必須以一種線程安全的方式實現。
    出於禮節,你可能希望在數據被修改的時候通過調用ContentResolver.notifyChange()來通知監聽者或者用戶。
    除瞭定義一個子類外,你仍然做如下幾步來簡化客戶端的編程,並且使得這個類更加有效:
      1.定義一個命名為CONTENT_URI的public static final Uri。這就是代表你的content provider要處理的數據的完整的content:URI字符串。
        你必須為這個字符串常量定義一個唯一的值。最好的方式就是使用content provider完成的類名(小寫).因此,對於一個Transpotation類,我們可以如下面一樣定義:       public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transportationprovider"); 
public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transportationprovider");        如果這個provider包含子表,那麼同樣需要為每一個子表定義CONTENT_URI常量,這些子表擁有相同的權限(也就是content provider的標示符),並且這些子表僅僅由他們的路徑來區分,如下面示例:
       content://com.example.codelab.transportationprovider/train  
content://com.example.codelab.transportationprovider/air/domestic  
content://com.example.codelab.transportationprovider/air/international 
        content://com.example.codelab.transportationprovider/train
 content://com.example.codelab.transportationprovider/air/domestic
 content://com.example.codelab.transportationprovider/air/international 對於content:URI的整體介紹,詳看本文最後的Content URI總結部分。
      2.定義content provider要返回給客戶端的列名。
      3.小心的記錄每一列數據的數據類型,客戶端需要這些信息來讀取數據。
      4.如果你要處理一個新的數據類型,你必須定義一個新的MIME類型作為你的COntentProvider.getType()方法的返回值。
      5.如果你想添加到表中的數據太大瞭——一個代表大位圖的文件——那麼暴露數據給客戶端的域必須包含一個content:URI字符串。
定義一個content provider。
    要讓系統知道你自己定義的content provider,那麼你應該在AndroidManifest文件中使用<provider>標簽進行定義。未在這裡定義的content provider對於系統來說都是不可見的。
    <provider>標簽的name屬性就是Conprovider子類的全名。authotities屬性就是標示provider的content:URI的權限部分。例如,如果ContentProvider子類的名稱是AutoInfoProvider,那麼<provider>元素可能如下面這樣定義:
<provider android:name="com.example.autos.AutoInfoProvider" 
     android:authorities="com.example.autos.autoinfoprovider"  
     . . . /> 
/provider> 
     <provider android:name="com.example.autos.AutoInfoProvider"
          android:authorities="com.example.autos.autoinfoprovider"
          . . . />
 </provider>    註意authorities屬性忽略瞭content:URI的路徑。例如:如果AutoINfoProvider對不同類型的autos或者不同的制造商擁有子表,   content://com.example.autos.autoinfoprovider/honda  
    content://com.example.autos.autoinfoprovider/gm/compact  
    content://com.example.autos.autoinfoprovider/gm/suv 
content://com.example.autos.autoinfoprovider/honda
 content://com.example.autos.autoinfoprovider/gm/compact
 content://com.example.autos.autoinfoprovider/gm/suv    這些路徑不應該在AndroidManifest文件中聲明。因為權限是對provider而言的,而不是對於路徑;你的provider應該可以以自己選擇的方式來解釋URI的路徑信息部分。
    <provider>的其他一些屬性可以設置讀取和改寫數據的權限,提供一個展示給用戶的圖標和一條信息,使能和禁止provider等等。如果content provider中的數據不需要在多個進程中進行同步,那麼就設置multiprocess屬性為“true”。這就允許每一個客戶端進程都實例化這個provider,建立必要的IPC。
Content URI總結
    下面是對於一個content URI的重要部分的概要介紹:

 

A.標準的前綴,表明這些數據是被content provider持有的。這個永遠不需要修改。
    B.URI所屬的部分,它是用來鑒別一個content provider的。對於第三方的應用程序,這必須是類的全名(必須是小寫)來確保它是唯一的。所屬是定義再<provider>元素的authorities屬性的:  <provider android:name=".TransportationProvider" 
          android:authorities="com.example.transportationprovider" 
          . . .  > 
<provider android:name=".TransportationProvider"
          android:authorities="com.example.transportationprovider"
          . . .  >    C.這部分是content provider用來決定是哪些類型的數據被請求。這個可以為空,也可以有幾個段那麼長。如果一個content provider僅僅暴露一種類型的數據(例如:僅僅是trains類型),那麼他就可以為空。如果一個content provider暴露幾種類型的數據,包括子類型,那麼它就有可能有幾段那麼長——例如:“land/bus”、“labd/train”、"sea/ship"和“sea/submarine”這四種可能的類型。

    D.被查詢的特殊記錄的ID。如果有,那麼這個就是被請求記錄的_ID值。如果請求不是對單個記錄,那麼這個段和斜杠都會被忽略:     

content://com.example.transportationprovider/trains 
    content://com.example.transportationprovider/trains

摘自  chenlong12580的專欄

發佈留言