Android四大組件之ContentProvider

Android四大組件之Activity
Android四大組件之Service
Android四大組件之BroadcastReceiver
Android四大組件之ContentProvider

一、內(nèi)容提供器簡介

內(nèi)容提供器(ContentProvider)主要用于在不同的應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)共享的功能,它提供了一套完整的機(jī)制,允許一個(gè)程序訪問另一個(gè)程序中的數(shù)據(jù),同時(shí)還能保證被訪問數(shù)據(jù)的安全性。目前,使用內(nèi)容提供器是Android實(shí)現(xiàn)跨進(jìn)程共享數(shù)據(jù)的標(biāo)準(zhǔn)方式。
不同于文件存儲和SharedPreferences存儲中的兩種全局可讀寫操作模式,內(nèi)容提供者可以選擇只對哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,從而保證我們程序中的隱私數(shù)據(jù)不會有泄露的風(fēng)險(xiǎn)。

二、訪問其他程序中的數(shù)據(jù)

內(nèi)容提供器的用法一般有兩種,一種是使用現(xiàn)有的內(nèi)容提供器來讀取和操作相應(yīng)程序中的數(shù)據(jù),另一種是創(chuàng)建自己的內(nèi)容提供器給我們程序的數(shù)據(jù)提供外部訪問接口。如果一個(gè)應(yīng)用程序通過內(nèi)容提供器對其數(shù)據(jù)提供了外部訪問接口,那么任何其他的應(yīng)用程序就都可以對這部分?jǐn)?shù)據(jù)進(jìn)行訪問。Android系統(tǒng)中自帶的電話簿、短信。多媒體等程序都提供了類似的訪問接口,這就使得第三方應(yīng)用程序可以充分地利用使用這些數(shù)據(jù)。

2.1 ContentResolver的基本用法

對于每一個(gè)應(yīng)用程序來說,如果想要訪問費(fèi)用提供器中共享的數(shù)據(jù),就一定要借助ContentResolver類,可以通過Context中的getContentResolver()方法獲取到該類的實(shí)例。
ContentResolver中提供了一系列的方法用于對數(shù)據(jù)進(jìn)行CRUD操作,其中insert()方法用于添加數(shù)據(jù),update()方法用于更新數(shù)據(jù),delete()方法用于刪除數(shù)據(jù),query()方法用于查詢數(shù)據(jù)。這種方式和SQLiteDatabase中方法基本類似,只不過在方法參數(shù)上稍微有些區(qū)別。
不同于SQLiteDatabase,ContentResolver中的增刪改查方法都是不接收表名參數(shù)的,而是使用一個(gè)Uri參數(shù)代替,這個(gè)參數(shù)被程蓉內(nèi)容URI。內(nèi)容URI給內(nèi)容提供器中的數(shù)據(jù)建立了唯一表示符,主要由兩部分組成:authority和path。authority是用于對不同的應(yīng)用程序進(jìn)行區(qū)分的,一般為了避免沖突,都會采用程序包名的方式進(jìn)行命名。例如com.monkey.contentprovider。path則是用于對同一引用程序中的不同表名做區(qū)分的,通常天驕到authority的后面。將authority和path進(jìn)行組合,再加上頭布局的協(xié)議聲明,就可以完成標(biāo)準(zhǔn)的URI格式寫法。如:
content://com.monkey.contentprovider/table1
content://com.monkey.contentprovider/table2
由此可以看出,內(nèi)容URI可以非常清楚的表達(dá)出我們想要訪問哪個(gè)程序中哪張表的數(shù)據(jù)。也正以為如此,ContentResolver中的增刪改查方法才都接收Uri對象作為參數(shù),因?yàn)槿绻褂帽砻脑?,系統(tǒng)將無法得知我們期望訪問的是哪個(gè)應(yīng)用程序里的表。
在得到內(nèi)容URI字符串后,我們還需要將它解析成Uri對象才可以最為參數(shù)穿日。具體解析方法如下:

Uri uri = Uri.parse("content://com.monkey.contentprovider/table1");

下來我們就可以使用這個(gè)Uri對象來查詢table1表中的數(shù)據(jù)了,代碼如下:

Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);

這些參數(shù)和sql中query()方法的參數(shù)很像,我們通過比較來學(xué)習(xí)下:

query()方法參數(shù) 對應(yīng)sql部分 描述
uri from table_name 指定查詢某個(gè)應(yīng)用程序下的某張表
projection select column1,column2 指定禪熏的列名
selection where column = value 指定where的約束條件
selectionArgs - 為where中的占位符提供具體的值
sortOrder order by column1 指定查詢結(jié)果的排序方式
2.1.1 查詢

查詢完成后返回的仍然是一個(gè)Cursor對象,這是我們可以將數(shù)據(jù)沖Cursor對象中逐個(gè)讀取出來。通過移動游標(biāo)的位置來遍歷Cursor的所有上,然后再取出每一行中相應(yīng)列的數(shù)據(jù)


if (null != cursor){
    while (cursor.moveToNext()){
        String colunm1 = cursor.getString(cursor.getColumnIndex("colunm1"));
        int colunm2 = cursor.getInt(cursor.getColumnIndex("colunm1"));
    }
    //使用完關(guān)閉
    cursor.close();
}

2.1.2 增加

向table1表中添加一條數(shù)據(jù),具體實(shí)現(xiàn)如下:


ContentValues contentValues = new ContentValues();
contentValues.put("colunm1","test");
contentValues.put("colunm2",1);
getContentResolver().insert(uri,contentValues);

2.1.3 修改

由此看出,是將待添加的數(shù)據(jù)組裝到ContentValues中,然后調(diào)用ContentResolver的insert()方法,將uri和ContentValues作為參數(shù)傳入。

更新數(shù)據(jù),修改column1的值,可以使用ContentResolver的update()方法實(shí)現(xiàn):


ContentValues contentValues = new ContentValues();
contentValues.put("colunm1","測試");
getContentResolver().update(uri,contentValues,"column1 = test",new String[]{"test","1"});

2.1.4 刪除

刪除數(shù)據(jù),調(diào)用ContentResolver的delete()方法刪除。具體代碼如下:


getContentResolver().delete(uri,"column1 = '測試'",new String[]{"測試","1"});

以上就是ContentResolver中的增刪改查,需要注意的是uri這個(gè)參數(shù)。

三、創(chuàng)建自己的內(nèi)容提供器

在上面,我們學(xué)習(xí)了如何在自己的程序中訪問其他應(yīng)用程序的數(shù)據(jù)。我們只需要獲取到該應(yīng)用程序的內(nèi)容uri,然后借助
ContentResolver進(jìn)行CRUD操作就可以實(shí)現(xiàn)。下面我們學(xué)習(xí)下怎樣創(chuàng)建自己的內(nèi)容提供器。

3.1 創(chuàng)建內(nèi)容提供器

要想實(shí)現(xiàn)跨程序共享數(shù)據(jù)的功能,官方推薦使用內(nèi)容提供器嗎,可以通過繼承ContentProvider的方式實(shí)現(xiàn)。ContentProvider類中有6個(gè)抽象方法,我們在使用子類繼承的時(shí)候,需要重寫這6個(gè)方法,代碼如下:

public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

下面了解下這6個(gè)方法:
1.onCreate()
初始化內(nèi)容提供者的時(shí)候調(diào)用。通常會在這里完成對數(shù)據(jù)庫的創(chuàng)建和升級操作。返回true,表示內(nèi)容提供器初始化成功,false則失敗。只有當(dāng)存在ContentResolver訪問我們程序數(shù)據(jù)時(shí),它才會被初始化。
2.query()
查詢數(shù)據(jù)。使用uri參數(shù)來確定查詢那張表,projection參數(shù)用于確定查詢那些列,selection和selectionArgs參數(shù)用戶約束查詢那些行,sortOrder參數(shù)用于對結(jié)果進(jìn)行排序,查詢結(jié)果存放在Curosr對象中返回。
3.insert()
添加數(shù)據(jù)。使用uri參數(shù)來確定要添加到的表,待添加的數(shù)據(jù)保存在values參數(shù)中。添加完成后,返回一個(gè)用于表示這條新紀(jì)錄的URI。
4.update()
修改已有數(shù)據(jù)。使用uri參數(shù)來確定更新那一張表中的數(shù)據(jù),新數(shù)據(jù)保存在values參數(shù)中,selection和selectionArgs參數(shù)用于約束那些行,將受影響的行數(shù)作為返回值返回。
5.delete()
刪除數(shù)據(jù)。使用uri參數(shù)來確定刪除哪一張表中的數(shù)據(jù),selection和selectionArgs參數(shù)用戶約束刪除那些行,被刪除的行數(shù)將作為參數(shù)值返回。
6.getType()
根據(jù)傳入的內(nèi)容uri來返回相應(yīng)的MIME類型(在下文中會介紹什么是MIME類型)。

3.2 UriMatcher類

在ContentProvider 中注冊uri ,根據(jù) uri 匹配 ContentProvider 中對應(yīng)的數(shù)據(jù)表。具體代碼如下:


    public static final int URI_CODE_TALBE1 = 1;
    public static final int URI_CODE_TALBE2 = 2;
    private static UriMatcher uriMatcher;

    static {
        //創(chuàng)建uriMaticher對象
        //常量UriMatcher.NO_MATCH  = 不匹配任何路徑的返回碼
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

        //在ContentProvider 中注冊URI(addURI())
        // 若URI資源路徑 = content://com.monkey.contentprovider/table1 ,則返回注冊碼URI_CODE_TALBE1
        uriMatcher.addURI("com.monkey.contentprovider", "table1", URI_CODE_TALBE1);
        // 若URI資源路徑 = content://com.monkey.contentprovider/table2 ,則返回注冊碼URI_CODE_TALBE2
        uriMatcher.addURI("com.monkey.contentprovider", "table2", URI_CODE_TALBE2); 
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        switch (uriMatcher.match(uri)) {
            case URI_CODE_TALBE1:
               // cursor = 查詢table1中的數(shù)據(jù)
                break;
            case URI_CODE_TALBE2:
                // cursor = 查詢table2中的數(shù)據(jù)
                break;
        }
      return cursor;
    }

我們在上面創(chuàng)建了UriMatcher實(shí)例,通過addaddURI()方法,將我們期匹配的內(nèi)容uri格式傳進(jìn)去,當(dāng)query()方法被調(diào)用的時(shí)候,將會通過UriMatcher的match()方法匹配傳進(jìn)來的uri,我們就可以知道調(diào)用方需要訪問什么數(shù)據(jù)了。對于insert()、update()、delete()方法的實(shí)現(xiàn)是類似的。

3.3 MIME數(shù)據(jù)類型

指定某個(gè)擴(kuò)展名的文件用某種應(yīng)用程序來打開
MIME類型組成
每種MIME類型 由2部分組成 = 類型 + 子類型

// 類型 = text、子類型 = html
text/html //超文本標(biāo)記語言文本
// 類型 = application、子類型 = vnd.android.package-archive
application/vnd.android.package-archive /APK文件(安卓系統(tǒng))

在內(nèi)容提供器中g(shù)etType()方法用于獲取Uri對象所對應(yīng)的MIME類型
android做了如下格式規(guī)定:

  • 如果內(nèi)容URI以路徑結(jié)尾,則后接android.cursor.dir/ ,如果內(nèi)容URI以id結(jié)尾,則后接android.cursor.item/。
  • 后面接上vnd.<authority>.<path>,必須以vnd開頭。

對于content://com.monkey.contentprovider/table1這個(gè)內(nèi)容URI,所對應(yīng)的MIME類型為android.cursor.dir/vnd.com.monkey.contentprovider/table1
對于content://com.monkey.contentprovider/table1/5這個(gè)內(nèi)容URI,所對應(yīng)的MIME類型為android.cursor.item/vnd.com.monkey.contentprovider/table1
在getType()中的實(shí)現(xiàn)如下:

    public static final int URI_CODE_TALBE1 = 1;
    public static final int URI_CODE_TALBE2 = 2;
    public static final int URI_CODE_TALBE1_ITEM = 3;
    private static UriMatcher uriMatcher;

    static {
        //創(chuàng)建uriMaticher對象
        //常量UriMatcher.NO_MATCH  = 不匹配任何路徑的返回碼
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

        //在ContentProvider 中注冊URI(addURI())
        // 若URI資源路徑 = content://com.monkey.contentprovider/table1 ,則返回注冊碼URI_CODE_TALBE1
        uriMatcher.addURI("com.monkey.contentprovider", "table1", URI_CODE_TALBE1);
        // 若URI資源路徑 = content://com.monkey.contentprovider/table2 ,則返回注冊碼URI_CODE_TALBE2
        uriMatcher.addURI("com.monkey.contentprovider", "table2", URI_CODE_TALBE2);
        // 若URI資源路徑 = content://com.monkey.contentprovider/table1/5 ,則返回注冊碼URI_CODE_TALBE1_ITEM
        uriMatcher.addURI("com.monkey.contentprovider", "table1/#", URI_CODE_TALBE1_ITEM);
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case URI_CODE_TALBE1:
                return "android.cursor.dir/vnd.com.monkey.contentprovider/table1";
            case URI_CODE_TALBE2:
                return "android.cursor.dir/vnd.com.monkey.contentprovider/table2";
            case URI_CODE_TALBE1_ITEM:
                return "android.cursor.item/vnd.com.monkey.contentprovider/table2";
            default:
                break;
        }
        return null;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容