做爲Android四大組件之一的ContentProvider,主要用於應用程序間數據共享。日常的開發中更多的是使用getContentResolver操做系統的多媒體數據庫(MediaProvider)。本文主要講述如何自定義ContentProvider及注意事項。java
public class SimpleContentProvider extends ContentProvider { private static final String TAG = "SimpleContentProvider"; // 若不匹配採用UriMatcher.NO_MATCH(-1)返回 private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH); private static final int CODE_NOPARAM = 1; //無參 // 匹配碼 private static final int CODE_PARAM = 2; //帶參數 static { // 對等待匹配的URI進行匹配操做,必須符合com.test.providers.SimpleContentProvider/artical格式 // 匹配返回CODE_NOPARAM,不匹配返回-1 MATCHER.addURI("com.test.providers.SimpleContentProvider", "artical", CODE_NOPARAM); // #表示數字 com.test.providers.SimpleContentProvider/artical/10 // 匹配返回CODE_PARAM,不匹配返回-1 MATCHER.addURI("com.test.providers.SimpleContentProvider", "artical/#", CODE_PARAM); } private DBOpenHelper dbOpenHelper; //操做數據庫 private final String ARTICAL_TABLE = "artical"; @Override public boolean onCreate() { // TODO Auto-generated method stub dbOpenHelper = new DBOpenHelper(getContext()); return true; } @Override public Uri insert(Uri uri, ContentValues cv) { // TODO Auto-generated method stub Log.i(TAG, "insert()"); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: long id = db.insert(ARTICAL_TABLE, null, cv); //若主鍵是自增的,則返回主鍵值;不然爲行號 Uri insertUri = ContentUris.withAppendedId(uri, id); return insertUri; default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub Log.i(TAG, "delete()"); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return db.delete(ARTICAL_TABLE, selection, selectionArgs); //刪除全部記錄 case CODE_PARAM: long id = ContentUris.parseId(uri); //取得Uri後面的數字 String where = "_id = " + id; if(null != selection && !(selection.trim()).equals("")){ where += "and " + selection; } return db.delete(ARTICAL_TABLE, where, selectionArgs); default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } @Override public int update(Uri uri, ContentValues cv, String selection, String[] selectionArgs) { // TODO Auto-generated method stub Log.i(TAG, "update()"); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return db.update(ARTICAL_TABLE, cv, selection, selectionArgs); //更新全部記錄 case CODE_PARAM: long id = ContentUris.parseId(uri); //取得Uri後面的數字 String where = "_id = " + id; if(null != selection && !(selection.trim()).equals("")){ where += " and " + selection; } return db.update(ARTICAL_TABLE, cv, where, selectionArgs); default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } /** * 返回對應的內容類型 * 若是返回集合的內容類型,必須以com.test.android.cursor.dir開頭 * 若是是單個元素,必須以com.test.android.cursor.item開頭 */ @Override public String getType(Uri uri) { // TODO Auto-generated method stub Log.i(TAG, "getType()"); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return "com.test.android.cursor.dir/artical"; case CODE_PARAM: return "com.test.android.cursor.item/artical"; default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) { // TODO Auto-generated method stub Log.i(TAG, "query()"); SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); switch (MATCHER.match(uri)) { case CODE_NOPARAM: return db.query(ARTICAL_TABLE, projection, selection, selectionArgs, null, null, orderBy); case CODE_PARAM: long id = ContentUris.parseId(uri); //取得Uri後面的數字 String where = "_id = " + id; if(null != selection && !(selection.trim()).equals("")){ where += "and " + selection; } return db.query(ARTICAL_TABLE, projection, where, selectionArgs, null, null, orderBy); default: throw new IllegalArgumentException("This is unknow uri: " + uri); } } }
該類提供了基本的增刪改查方法,能夠看出,實際操做的是數據庫。其中DBOpenHelper類是一個繼承自SQLiteOpenHelper的自定義類,裏面包含基本的數據庫建立、表的建立等操做,比較簡單,此處就不貼代碼了。android
一、已經建立了自定義類,接下來須要將其註冊到AndroidManifest.xml中:數據庫
<!-- 自定義的一些權限,須要發佈才能被其餘應用使用 -->
<permission android:name="android.permission.WRITE_SCP" />
<permission android:name="android.permission.READ_SCP"/>
<provider android:name="com.test.contentprovider.SimpleContentProvider" android:authorities="com.test.providers.SimpleContentProvider" android:exported="true" android:readPermission="android.permission.READ_SCP" android:writePermission="android.permission.WRITE_SCP" > <path-permission android:pathPrefix="/search_query" android:readPermission="android.permission.GLOBAL_SEARCH" /> </provider>
說明:ide
android:exported="true",容許在其餘應用中訪問該Provider;若設置爲false時,只有同一個應用程序的組件或帶有相同用戶ID的應用程序才能啓動或綁定該服務。
android:authorities 受權信息,用於區分不一樣的ContentProvider
android:permission 聲明訪問provider的權限測試
將權限細化,可分爲下述兩個:
android:readPermission 表示讀權限,
android:writePermission 寫權限spa
【疑問】細化後的權限比permission優先級高。
可是,測試結果顯示:僅添加readPermission或writePermission權限,在另外一應用中讀寫provider均可以,那細化的做用在哪裏?在網上也沒有找到相關的介紹,誰若知道,可留言告知,謝謝!操作系統
二、既然聲明瞭權限,那麼其餘應用訪問該provider時,也須要AndroidManifest.xml申請相應權限,不然沒法訪問。code
<uses-permission android:name="android.permission.READ_SCP"/> <uses-permission android:name="android.permission.WRITE_SCP"/>
三、程序中訪問:xml
Uri uri = Uri.parse("content://com.test.providers.SimpleContentProvider/artical"); ContentResolver resolver = getContentResolver(); ContentValues values = new ContentValues(); values.put("title", "testcase1 "); values.put("content", "ContentProvider-put"); resolver.insert(uri, values);
以上便是一個簡單的訪問自定義ContentProvider的例子。blog
一般狀況下,ContentProvider存儲數據操做的仍是數據庫,只是進行了封裝。不管是自定義類仍是系統原生的類,都是採用ContentProvider + SQLiteOpenHelper實現的。(自定義類繼承ContentProvider,自定義類繼承SQLiteOpenHelper實現對應數據庫操做。)例:系統的MediaProvider、SettingsProvider或者上述自定義SimpleContentProvider均是如此。
可是,這並不表示ContentProvider只能操做數據庫,也能夠是文件等,不過,我沒測試過,感興趣的可自行驗證。
總結Cursor遍歷ContentProvider時,經常使用的幾種方式:其實就是for()與while的使用。
1 ContentResolver resolver = getContentResolver(); 2 Cursor cursor = resolver.query(uri, null, null, null, null); 3 if(cursor != null && cursor.getCount() > 0){ 4 .....//遍歷代碼位置 5 } 6 7 方法一: 8 for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()){ 9 ...... 10 } 11 12 方法二: 13 while(cursor.moveToNext()){ 14 ....//具體操做 15 } 16 17 方法三: 18 19 cursor.moveToFirst(); //移動到第一行;默認cursor下標在-1 20 while(!cursor.isAfterLast()){ 21 ....... 22 .......//具體操做 23 cursor.moveToNext(); 24 } 25 26 方法四: 27 cursor.moveToFirst(); 28 do{ 29 ...... 30 }while(cursor.moveToNext());
上述代碼看似很簡單,可是在自測的過程卻出現了奇怪的問題:
使用while偶現幾回丟失數據的狀況(即當前有三條數據,只讀取了兩條;可是此時(使用while)若將記錄的id進行讀取則能成功讀取三條數據),具體緣由未找到,以後又屢次測試則沒法復現了,所以在此處先記下該種狀況,有知道的能夠留言告知,謝謝。