android ContentResolver詳解 http://www.2cto.com/kf/201207/144022.html


在Android 應用程序之間數據共享—-ContentResolver中,已經說明了Android是如何實現應用程序之間數據共享的,並詳細解析瞭如何獲取其他應用程序共享的數據。ContentProviders存儲和檢索數據,通過它可以讓所有的應用程序訪問到,這也是應用程序之間唯一共享數據的方法。那麼如何將應用程序的數據暴露出去?
        通過以前文章的學習,知道ContentResolver是通過ContentProvider來獲取其他與應用程序共享的數據,那麼ContentResolver與ContentProvider的接口應該差不多的。
其中ContentProvider負責
組織應用程序的數據;
向其他應用程序提供數據;
ContentResolver則負責
獲取ContentProvider提供的數據;
修改/添加/刪除更新數據等;
ContentProvider 是如何向外界提供數據的?
       Android提供了ContentProvider,一個程序可以通過實現一個ContentProvider的抽象接口將自己的數據完全暴露出去,而且ContentProviders是以類似數據庫中表的方式將數據暴露,也就是說ContentProvider就像一個“數據庫”。那麼外界獲取其提供的數據,也就應該與從數據庫中獲取數據的操作基本一樣,只不過是採用URI來表示外界需要訪問的“數據庫”。至於如何從URI中識別出外界需要的是哪個“數據庫”,這就是Android底層需要做的事情了,不在此詳細說。簡要分析下ContentProvider向外界提供數據操作的接口:
query(Uri, String[], String, String[], String)
insert(Uri, ContentValues)
update(Uri, ContentValues, String, String[])
delete(Uri, String, String[])
       這些操作與數據庫的操作基本上完全一樣,在此不詳細說,具體的解析可以參考Android Sqlite解析篇中的詳細說明。需要特殊說明的地方是URI:

        在URI的D部分可能包含一個_ID ,這個應該出現在SQL語句中的,可以以種特殊的方式出現,這就要求我們在提供數據的時候,需要來額外關注這個特殊的信息。Android  SDK推薦的方法是:在提供數據表字段中包含一個ID,在創建表時INTEGER PRIMARY KEY AUTOINCREMENT標識此ID字段。
ContentProvider 是如何組織數據的?
組織數據主要包括:存儲數據,讀取數據,以數據庫的方式暴露數據。數據的存儲需要根據設計的需求,選擇合適的存儲結構,首選數據庫,當然也可以選擇本地其他文件,甚至可以是網絡上的數據。數據的讀取,以數據庫的方式暴露數據這就要求,無論數據是如何存儲的,數據最後必須以數據的方式訪問。
可能還有2個問題,是需要關注的。
ContentProvider是什麼時候創建的,是誰創建的?訪問某個應用程序共享的數據,是否需要啓動這個應用程序?這個問題在Android SDK中沒有明確說明,但是從數據共享的角度出發,ContentProvider應該是Android在系統啓動時就創建了,否則就談不上數據共享了。這就要求在AndroidManifest.XML中使用<provider>元素明確定義。
可能會有多個程序同時通過ContentResolver訪問一個ContentProvider,會不會導致像數據庫那樣的“髒數據”?這個問題一方面需要數據庫訪問的同步,尤其是數據寫入的同步,在AndroidManifest.XML中定義ContentProvider的時候,需要考慮是<provider>元素multiprocess屬性的值;另外一方面Android在ContentResolver中提供了notifyChange()接口,在數據改變時會通知其他ContentObserver,這個地方應該使用了觀察者模式,在ContentResolver中應該有一些類似register,unregister的接口。
Android是如何實現應用程序之間數據共享的?我們以前談到外界的程序可以通過ContentResolver接口訪問ContentProvider提供的數據,今天我們來談下如何創建自己的ContentProvider來實現應用程序之間的數據共享。

      一個應用程序可以創建自己的數據,這個數據對該應用程序來說是私有的,外界更本看不到,也不知道數據是如何 存儲的,或者是使用數據庫還是使用文件,還是通過網上獲得,這些一切都不重要,重要的是外界可以通過這一套標準及統一的接口和這個程序裏的數據打交道,例 如:添加(insert)、刪除(delete)、查詢(query)、修改(update)。

      Android爲我們提供了ContentProvider來實現數據的共享,一個程序如果想讓別的程序可以操作自己的數據,就定義自己的 ContentProvider,然後在AndroidManifest.xml中註冊,其他application可以通過獲取 ContentResolver通過Uri來操作上一程序的數據。

      Android中的電話本等數據就是通過ContentProvider實現數據共享的,系統中有很多已經存在的共享Uri。我們可以使用ContentResolver通過Uri來操作不同表的數據;如Contacts.People.CONTENT_URI。

     查詢Content Provider的方法有兩個:ContentResolver的query() 和 Activity 對象的 managedQuery(),二者接收的參數均相同,返回的都是Cursor 對象,唯一不同的是 使用managedQuery 方法可以讓Activity 來管理 Cursor 的生命週期。
    被管理的Cursor 會在 Activity進入暫停狀態的時候調用自己的 deactivate 方法自行卸載,而在Activity回到運行狀態時會調用自己的requery 方法重新查詢生成的Cursor對象。如果一個未被管理的Cursor對象想被Activity管理,可以調用Activity的 startManagingCursor方法來實現。
 
什麼是URI?

將其分爲A,B,C,D 4個部分:

A:標準前綴,用來說明一個Content Provider控制這些數據,無法改變的;"content://"

B:URI的標識,它定義了是哪個Content Provider提供這些數據。對於第三方應用程序,爲了保證URI標識的唯一性,它      必須是一個完整的、小寫的 類名。這個標識在 元素的 authorities屬性中說明:一般是定義該ContentProvider的包.      類的名稱 ;"content://com.android.calendar" (系統日曆的URI)

C:路徑,URI下的某一個Item,就像網站一樣,主網頁下包含很多小網頁。這裏通俗的講就是你要操作的數據庫中表的名      字,或者你也可以自己定義,記得在使用的時候保持一致就ok了;"content://com.android.calendar/calendars"

D:如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應的數據,如果沒有ID,就表示返回全部;                            "content://com.android.calendar/calendars/#" #表示數據id(#代表任意數字)
     "content://com.android.calendar/calendars/*" *來匹配任意文本
 
UriMatcher:用於匹配Uri,它的用法如下:

     1.首先把你需要匹配Uri路徑全部給註冊上。
        1.常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼(-1)。 UriMatcher                                       uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        2.如果match()方法匹content://com.android.calendar/calendars路徑,返回匹配碼爲1
       uriMatcher.addURI(“content://com.android.calendar”, “calendars”, 1);
        3.添加需要匹配uri,如果匹配就會返回匹配碼 //如果match()方法匹配
  
content://com.android.calendar/calendars/23路徑,返回匹配碼爲2
uriMatcher.addURI(“content://com.android.calendar”, “calendars/#”, 2);
 
      2.註冊完需要匹配的Uri後,就可以使用uriMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹
配就返回匹配碼,匹配碼是調用 addURI()方法傳入的第三個參數,假設匹配
content://com.android.calendar/calendars路徑,返回的匹配碼爲1。
       ContentUris:用於獲取Uri路徑後面的ID部分,它有兩個比較實用的方法:
withAppendedId(uri, id)用於爲路徑加上ID部分
parseId(uri)方法用於從路徑中獲取ID部分
 
以下是一個例子的簡單說明:
 
Xml代碼 
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    android:versionCode="1" android:versionName="1.0" 
    android:installLocation="internalOnly" package="com.calendarwidget"> 
 
    <application android:icon="@drawable/icon" android:label="@string/app_name"> 
                <!-- 告訴系統,你的應用程序有provider組件 --> 
        <provider android:name=".CalendarProvider" 
            android:authorities="com.calendarwidget.provider" 
        /> 
             
    </application> 
    <uses-permission android:name="android.permission.READ_CALENDAR" /> 
    <uses-permission android:name="android.permission.WRITE_CALENDAR" /> 
</manifest> 
 public class CalendarProvider extends ContentProvider
Java代碼 

 
    private static final String URI_AUTHORITY = "com.calendarwidget.provider"; 
     
    public static final String URI_PATH = "RecordSet"; //只是填充,沒有作用 
    public static final String URI_PATH2 = "RecordSet/#";//只是填充,沒有作用 
     
    public static final Uri CONTENT_URI = Uri.parse("content://" 
            + URI_AUTHORITY + "/" + URI_PATH); 
     
    private static final UriMatcher sMatcher; 
     
    public static final int ALL_EVENT_RECORDS = 0; 
     
    static 
    { 
        sMatcher = new UriMatcher(UriMatcher.NO_MATCH); 
        sMatcher.addURI(URI_AUTHORITY, URI_PATH, ALL_EVENT_RECORDS); 
        sMatcher.addURI(URI_AUTHORITY, URI_PATH2, ALL_EVENT_RECORDS); 
    } 
     
    private Context mContext; 
     
    @Override 
    public boolean onCreate() 
    { 
        if (mContext == null) 
        { 
            mContext = getContext(); 
        } 
        return true; 
    } 
     
    @Override 
    public Cursor query(Uri uri, String[] projection, String selection, 
            String[] selectionArgs, String sortOrder) 
    { 
        //匹配碼 
                int match = sMatcher.match(uri); 
        Cursor cur = null; 
        switch (match) 
        { 
            case ALL_EVENT_RECORDS: 
                cur = loadAllCalendarEvent(this); 
                break; 
             
            default: 
                break; 
        } 
        return cur; 
    } 
     
    private MatrixCursor loadAllCalendarEvent(CalendarProvider calendarProvider) 
    { 
        MatrixCursor mc = new MatrixCursor(CalendarConstants.PROJECTION); 
        Cursor calendarCursor = null; 
        try 
        { 
            calendarCursor = calendarProvider 
                    .getContext() 
                    .getContentResolver().query("content://com.android.calendar/calendars", 
                            null, null,  
                            null, null); / 
             
            while (calendarCursor.moveToNext()) 
            { 
                 
                    //TODO 
                                ..... 
                mc.addRow(rowObject); 
            } 
            return mc; 
        } finally 
        { 
            calendarCursor.close(); 
        } 
    } 
     
    @Override 
    public String getType(Uri uri) 
    { 
        return null; 
    } 
     
    @Override 
    public Uri insert(Uri uri, ContentValues values) 
    { 
        return null; 
    } 
     
    @Override 
    public int delete(Uri uri, String selection, String[] selectionArgs) 
    { 
        return 0; 
    } 
     
    @Override 
    public int update(Uri uri, ContentValues values, String selection, 
            String[] selectionArgs) 
    { 
        return 0; 
    } 

 
    關於getType使用提示:
 
   <intent-filter android:label="@string/resolve_edit">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.EDIT" />
                <action android:name="com.android.notepad.action.EDIT_NOTE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>
 
      我們很容易看出action和category是很容易匹配的,而我們傳的Uri的數據怎麼匹配呢,這時系統就會去調用你定義的ContentProvider中的getType,取得相關的返回值來和上面的data串進行匹配,當然getType的返回結果你是需要自己去定義的。
但在程序中你也可以自己知道data的類型,就直接匹配了:intent.setType(type);
作者:chang_xing


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章