自定義ContentProvider的使用


    作為Android四大組件之一的ContentProvider,主要用於應用程序間數據共享。平常的開發中更多的是使用getContentResolver操作系統的多媒體數據庫(MediaProvider)。本文主要講述如何自定義ContentProvider及注意事項。

一、自定義SimpleContentProvider.java,繼承ContentProvider。

代碼如下:

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的自定義類,里面包含基本的數據庫創建、表的創建等操作,比較簡單,此處就不貼代碼了。

具體步驟:

1、已經創建了自定義類,接下來需要將其注冊到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>

說明:

android:exported="true",允許在其他應用中訪問該Provider;若設置為false時,只有同一個應用程序的組件或帶有相同用戶ID的應用程序才能啟動或綁定該服務。
android:authorities  授權信息,用於區分不同的ContentProvider
android:permission    聲明訪問provider的權限

將權限細化,可分為下述兩個:
android:readPermission        表示讀權限,
android:writePermission        寫權限

【疑問】細化后的權限比permission優先級高。
但是,測試結果顯示:僅添加readPermission或writePermission權限,在另一應用中讀寫provider都可以,那細化的作用在哪里?在網上也沒有找到相關的介紹,誰若知道,可留言告知,謝謝!

2、既然聲明了權限,那么其他應用訪問該provider時,也需要AndroidManifest.xml申請相應權限,否則無法訪問。

    <uses-permission android:name="android.permission.READ_SCP"/>
    <uses-permission android:name="android.permission.WRITE_SCP"/>

3、程序中訪問:

            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的例子。

4、總結:

    通常情況下,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進行讀取則能成功讀取三條數據),具體原因未找到,之后又多次測試則無法復現了,因此在此處先記下該種情況,有知道的可以留言告知,謝謝。

 

 

 

 

 

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM