ContentProvider的深入淺出
ContentProvider作為Android應用程式中的四大元件之一,是一個特殊的儲存資料的型別,它提供了一套標準的介面用來獲取以及操作資料,並實現了在各個應用程式之間資料共享。
構成
關鍵類:Content Resolver 、Cursor(表示返回結果) 、URI(標識Provider,作為Resolver的查詢引數)
-
內容解析器(Content Resolver)
在ContentProvider使用過程中,需要借用ContentResolver物件作為代理,間接操作ContentProvider來獲取資料。可以通過Context直接獲取:
ContentResolver cr = content.getContentResovler();
可以通過Content Resolver與任意Content Provider進行互動。
一般,每個Content Resolver都只有一個例項(單例模式),但可以與多個在不同應用或程序中的Content Resolver進行通訊。程序間通訊可以通過ContentResolver和ContentProvider進行。
- 資料模型(data model)
Content Provider將其資料以資料庫中簡單的表示模型展示,一行表示一條記錄,一列表示資料的某種型別和含義。每條記錄都有一個數值_ID欄位,唯一地標識一條記錄。
查詢返回結果以Cursor(遊標,類似於指標)。 - 統一資源標識(URI)
URI代表了要操作的資料,URI主要包含兩部分資訊:1.需要操作的ContentProvider,2.對ContentProvider中的什麼資料進行操作,一個URI由以下幾個部分組成:
1.scheme:ContentProvider(內容提供者)的scheme由Android規定為:content://。
2.主機名(Authority):用於唯一標識這個ContentProvider,外部呼叫者可以根據這個標識來找到它。
3.路徑(path)可以用來標識我們要操作的資料,路徑的構建應根據業務而定。
android定義許多CONTENT_URI 常量用於標識自帶的Content Provider。如
android.provider.Contacts.Phones.CONTENT_URI android.provider.Contacts.Photos.CONTENT_URI
建立自己的ContentProvider
我們大家都知道讓自己的資料和其它應用程式共享有兩種方式:建立自己的Content Provider (即繼承自Content Provider的子類)或者是將自己的資料新增到已有的Content Provider中去,後者需要保證現有的Content Provider和自己的資料型別相同並且具有該 Content Provider的寫入的許可權。
如果需要建立一個Content Provider,則需要進行的工作主要分為以下3個步驟。
第一步:建立資料的儲存系統
資料的儲存系統可以由開發人員任意決定,一般來講,大多數的Content Provider都通過Android的檔案儲存系統或SQLite資料庫建立自己的資料儲存系統。
第二步:擴充套件 ContentProvider類
開發一個繼承自ContentProvider類的子類程式碼來擴充套件ContentProvider類,在這個步驟主要的工作是將要共享的資料包裝並以ContentResolver和Cursor物件能夠訪問到的形式對外展示。具體來說需要實現ContentProvider 類中的6個抽象方法。
ContentProvider示例程式(部分):
/** * Created by zhuyifei on 2016/7/13. */ public class MyContentProvider extends ContentProvider { private static final int ACCOUNTS = 1; private static final int ACCOUNTS_ID = 2; private static final UriMatcher sUriMatcher; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(TableContracts.AUTHORITY, TableContracts.Accounts.TABLE_PATH, ACCOUNTS); sUriMatcher.addURI(TableContracts.AUTHORITY, TableContracts.Accounts.TABLE_PATH_WITH_PARAM, ACCOUNTS_ID); } @Override public boolean onCreate() { return false; } @Nullable @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Nullable @Override public String getType(Uri uri) { return null; } @Nullable @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; } }
以上方法將會在ContentResolver物件中呼叫,所以很好地實現這些抽象方法會為ContentResolver提供一個完善的外部介面。除了實現抽象方法外,還可以做一些提高可用性的工作。
定義一個 URI 型別的靜態常量,命名為CONTENT_URI。 必須為該常量物件定義一個唯一的URI字串,一般的做法是將ContentProvider子類的全稱類名作為URI字串,如:“自定義”。
定義每個欄位的列名,如果採用的資料庫儲存系統為SQLite 資料庫,資料表列名可以採用資料庫中表的列名。不管資料表中有沒有其他的唯一標識一個記錄的欄位,都應該定義一個”_id”欄位來唯一標識一個記錄。模式使用 “INTEGER PRIMARY KEY AUTOINCREMENT” 自動更新 一般將這些列名字串定義為靜態常量, 如”_id”欄位名定義為一個名為”_ID” 值為“_id”的靜態字串物件。
第三步:在應用程式的AdnroidManifest.xml 檔案中宣告Content Provider元件。
建立好一個Content Provider必須要在應用程式的AndroidManifest.xml 中進行宣告,否則該Content Provider對於Android系統將是不可見的。如果有一個名為MyProvider的類擴充套件了 ContentProvider類,宣告該元件的程式碼如下:
<provider name=".MyContentProvider(自定義)" authorities="com.sjtu.demo.provider(TableContracts.AUTHORITY保持一致)" android:exported="false" ...../> <!-- 為<provider>標記新增name、authorities等屬性-->
- name屬性為ContentProvider子類的全稱類名
- authorities屬性唯一標識了一個ContentProvider。還可以通過setReadPermission()和* setWritePermission() 來設定其操作許可權。當然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission屬性來控制其許可權。
- exported屬性表示是否支援其他應用呼叫當前元件,true表示支援,false表示不支援。預設值:如果包含intent-filter預設值為true;否則為false
- 注意:因為ContentProvider可能被不同的程序和執行緒呼叫,所以這些方法必須是執行緒安全的。
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。