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