ContentProvider 基礎(chǔ)知識

ContentProvider 用戶跨進程訪問數(shù)據(jù),通常和數(shù)據(jù)庫以及ContentResolver配合使用,可以保證數(shù)據(jù)的安全性。

一、ContentResolver

對于每一個應(yīng)用程序來說,如果想要訪問內(nèi)容提供器中共享的數(shù)據(jù),就一定要借助ContentResolver類,可以通過Context中的getContentResolver()方法獲取到該類的實例。 ContentResolver中提供了一系列的方法用于對數(shù)據(jù)進行CRUD操作,其中insert()方法用于添加數(shù)據(jù),update()方法用于更新數(shù)據(jù),delete()方法用于刪除數(shù)據(jù),query()方法用于查詢數(shù)據(jù)。有沒有似曾相識的感覺?沒錯,SQLiteDatabase中也是使用的這幾個方法來進行 CRUD 操作的,只不過它們在方法參數(shù)上稍微有一些區(qū)別。不同于SQLiteDatabase,ContentResolver中的增刪改查方法都是不接收表名參數(shù)的,而是使用一個 Uri參數(shù)代替,這個參數(shù)被稱為內(nèi)容 URI,下面會詳細介紹它的格式定義。在得到了內(nèi)容URI字符串之后,我們還需要將它解析成Uri對象才可以作為參數(shù)傳入。 只需要調(diào)用 Uri.parse()方法,就可以將內(nèi)容URI字符串解析成 Uri對象:

Uri uri = Uri.parse("content://com.example.app.provider/table1") 

再調(diào)用contentResolver的CURD方法就可完成對應(yīng)的操作。

Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);

二、內(nèi)容 URI

無論是通過ContentResolver訪問數(shù)據(jù),還是實現(xiàn)自定義的ContentProvider都需要內(nèi)容URI才能夠通信。URI給內(nèi)容提供器中的數(shù)據(jù)建立了唯一標(biāo)志符,基本格式如下:

content://Authority/Path/Id

content:// 固定格式
Authority:權(quán)限,用于對不同的應(yīng)用程序進行區(qū)分,一般為了避免沖突,都會采用包名.provider
Path: 路徑,用于對同一個程序中的不同表做區(qū)分
Id: 數(shù)據(jù)Id,用于區(qū)分表中的不同數(shù)據(jù)
URI的格式主要有如下2種:

   content://com.example.app.provider/table 
   content://com.example.app.provider/table1/1 

以路徑結(jié)尾表示期望訪問該表中所有的數(shù)據(jù), 以 id結(jié)尾就表示期望訪問該表中擁有相應(yīng) id的數(shù)據(jù)。我們可以使用通配符的方式來分別匹 配這兩種格式的內(nèi)容 URI,規(guī)則如下:

1.*:表示匹配任意長度的任意字符
2.#:表示匹配任意長度的數(shù)字

所以,一個能夠匹配任意表的內(nèi)容 URI格式就可以寫成:

content://com.example.app.provider/* 

而一個能夠匹配 table1表中任意一行數(shù)據(jù)的內(nèi)容 URI格式就可以寫成

content://com.example.app.provider/table1/#

我們再借助UriMatcher這個類就可以輕松地實現(xiàn)匹配內(nèi)容URI的功能。UriMatcher中提供了一個addURI()方法,這個方法接收三個參數(shù),可以分別把權(quán)限、路徑和一個自定義代碼傳進去。這樣,當(dāng)調(diào)用UriMatcher的match()方法時,就可以將一個 Uri對象傳入,返回值是某個能夠匹配這個Uri對象所對應(yīng)的自定義代碼,利用這個代碼,我們就可以判斷出調(diào)用方期望訪問的是哪張表中的數(shù)據(jù)了。

三、ContentProvider方法介紹

通過繼承ContentProvider,并且實現(xiàn)抽象方法就可完成自定義的ContentProvider共享數(shù)據(jù)了。

1.onCreate()

初始化內(nèi)容提供器的時候調(diào)用。通常會在這里完成對數(shù)據(jù)庫的創(chuàng)建和升級等操作,返回true表示內(nèi)容提供器初始化成功,返回false 則表示失敗。注意,只有當(dāng)存在 ContentResolver嘗試訪問我們程序中的數(shù)據(jù)時,內(nèi)容提供器才會被初始化。

2.query()

從內(nèi)容提供器中查詢數(shù)據(jù)。使用uri參數(shù)來確定查詢哪張表,projection參數(shù)用于確定查詢哪些列,selection和selectionArgs參數(shù)用于約束查詢哪些行,sortOrder參數(shù)用于對結(jié)果進行排序,查詢的結(jié)果存放在Cursor對象中返回。

3.insert()

向內(nèi)容提供器中添加一條數(shù)據(jù)。使用uri參數(shù)來確定要添加到的表,待添加的數(shù)據(jù)保存在values參數(shù)中。添加完成后,返回一個用于表示這條新記錄的 URI。

4.update()

更新內(nèi)容提供器中已有的數(shù)據(jù)。使用uri參數(shù)來確定更新哪一張表中的數(shù)據(jù),新數(shù)據(jù)保存在values參數(shù)中,selection和selectionArgs參數(shù)用于約束更新哪些行,受影響的 行數(shù)將作為返回值返回。

5.delete()

從內(nèi)容提供器中刪除數(shù)據(jù)。使用uri參數(shù)來確定刪除哪一張表中的數(shù)據(jù),selection和selectionArgs參數(shù)用于約束刪除哪些行,被刪除的行數(shù)將作為返回值返回。

6.getType()

根據(jù)傳入的內(nèi)容 URI來返回相應(yīng)的 MIME類型。它是所有的內(nèi)容提供器都必須提供的一個方法,用于獲取Uri對象所對應(yīng)的MIME類型。一個內(nèi)容URI所對應(yīng)的MIME字符串主要由三部分組分,Android對這三個部分做了如下格式規(guī)定。
1. 必須以vnd開頭。
2. 如果內(nèi)容URI以路徑結(jié)尾,則后接android.cursor.dir/,如果內(nèi)容URI以id結(jié)尾,則后接android.cursor.item/
3. 最后接上 vnd.authority.path

所以,對于 content://com.example.app.provider/table1這個內(nèi)容 URI,它所對應(yīng)的 MIME 類型就可以寫成:

vnd.android.cursor.dir/vnd.com.example.app.provider.table1 

對于 content://com.example.app.provider/table1/1這個內(nèi)容 URI,它所對應(yīng)的 MIME類型 就可以寫成:

 vnd.android.cursor.item/vnd.com.example.app.provider.table1

四、call 方法

上面的方法都是訪問或修改數(shù)據(jù)庫的,如果需要跨進程訪問里一個應(yīng)用其他數(shù)據(jù),例如sharePreference數(shù)據(jù),可以通過call方法來調(diào)用自定義的函數(shù)。

1.provider中復(fù)寫call 方法,并且添加自定義的方法

@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
    switch (method){
        case "getData":
            return getData();
        default:
            break;
    }
    return null;
}

//自定義方法
public Bundle getData(){
    Bundle b = new Bundle();
    b.putString("name","call getData");
    return b;
}

2.查詢方傳入要調(diào)用的自定義方法名名稱和參數(shù)

// uri格式最后一定要帶一個 /
Uri uriCall = Uri.parse("content://" + AUTHORITY + "/");
Bundle b = getContentResolver().call(uriCall, "getData", null, null);
Log.i(TAG, ""+b.get("name"));

五、MatrixCursor

ContentProvider的Query方法返回的是一個cursor,如果要對cursor中的數(shù)據(jù)做處理后再返回給查詢的一方,可以通過MatrixCursor 對現(xiàn)有數(shù)據(jù)封裝后返回。

Cursor cursor1 = db.query("users", null, "id = ?", new String[]{"1"},null, null, null);
MatrixCursor m = new MatrixCursor(new String[]{"c1", "c2", "c3"});
while(cursor1.moveToNext()) {
    String name = cursor1.getString(1);
    int age = cursor1.getInt(2);
    String address = cursor1.getString(3);
    Log.i(TAG, name + "age = "+age + "address = "+address);
    m.addRow(new Object[]{name, age, address});
}
return m

Demo:

參考資料:
第一行代碼——Android 郭霖
https://stackoverflow.com/questions/17224766/what-is-getcontentresolver-call-and-how-to-use-it

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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