概述
ContentProvider 主要用于在不同的應用程式之間實作資料共享的功能,提供了一套完整的機制,允許一個程式訪問另一個程式的資料,同時還能保證資料的安全
ContentProvider 的用法一般有兩種:一種是使用現在的 ContentProvider 讀取和操作相應程式中的資料;另一種是創建自己的 ContentProvider,給程式的資料提供外部訪問介面
ContentResolver
對于一個應用程式來說,要想訪問 ContentProvider 中共享的資料,就要借助 ContentResolver 類,可以通過 Context 中的 getContentResolver() 方法獲取該類的實體
ContentResolver 提供了一系列方法用于對資料進行增刪改查,與 SQLiteDatabase 相似,但不同的是,ContentResolver 中的增刪改查方法不接收表名引數,而是使用一個 Uri 引數代替,這個引數稱為內容 URI
內容 URI 給 ContentProvider 中的資料建立了唯一識別符號,它主要由兩部分組成:authority 和 path,authority 用于區分不同的應用程式,一般采用應用包名的方式進行命名,比如某個應用的包名是 com.example.app,那么 authority 就可以命名為 com.example.app.provider,path 則是用于對同一程式中不同的表做區分,通常添加到 authority 的后面,比如某個應用程式的資料庫存在兩個 table1 和 table2,這時可以將 path 分別命名為 /table1 和 /table2,然后將 authority 和 path 進行組合,內容 URI 就變成了 com.example.app.provider/table1 和 com.example.app.provider/table2,我們還需在字串的頭部加上協議宣告,因此內容 URI 最標準格式如下:content://com.example.app.provider/table1 和 content://com.example.app.provider/table1
在得到內容 URI 字串之后,我們還需要將它決議成 Uri 物件才可以作為引數傳入,代碼如下:
val uri = Uri.parse("content://com.example.app.provider/table1")
再使用這個 Uri 物件查詢 table1 表中的資料,代碼如下:
val cursor = contentResolver.query(
uri,
projection,
selection,
selectionArgs,
sortOrder)
下表是 query() 方法的引數說明
| query() 方法引數 | 對應 SQL 部分 | 描述 |
|---|---|---|
| uri | from table_name | 指定查詢某個應用程式下的某一張表 |
| projection | select column1, column2 | 指定查詢的列名 |
| selection | where column = value | 指定 where 的約束條件 |
| selectionArgs | 為 where 中的占位符提供具體的值 | |
| sortOrder | order by column1, column2 | 指定查詢結果的排序方式 |
根據回傳的 Cursor 物件,我們就可以將資料從 Cursor 物件中遍歷讀取出來了
while(cursor.moveToNext()) {
val column1 = cursor.getString(cursor.getColumnIndex("column1"))
val column2 = cursor.getInt(cursor.getColumnIndex("column2"))
}
cursor.close()
掌握了查詢操作,剩下的增加、修改、洗掉操作就更不在話下了
查詢操作是將待添加的資料組裝到 ContentValues 中,然后呼叫 ContentResolver 的 insert() 方法
val values = contentValuesOf("column1" to "next", "column2" to 1)
contentResolver.insert(uri, values)
如果我們想要更新這條新添加的資料,然后將 column1 的值清空,可以借助 ContentResolver 的 update() 方法實作
val values = contentValuesOf("column1" to "")
contentResolver.update(uri, values, "column1 = ? and column2 = ?", arrayOf("text", "1"))
最后,可以呼叫 ContentResolver 的 delete() 方法將這條資料洗掉
contentResolver.delete(uri, "column2 = ?", arrayOf("1"))
ContentProvider
如果希望自己的程式的資料能被共享,可以新建一個類去繼承 ContentProvider 的方式去實作,ContentProvider 類中有六個抽象方法,我們在使用子類繼承它的時候,需要將這六個方法全部重寫
class MyProvider : ContentProvider() {
/*
* 向 ContentProvider 中添加一條資料
*/
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
/*
* 從 ContentProvider 中查詢資料
*/
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
return null
}
/*
* 初始化 ContentProvider 時呼叫,通常在這里完成對資料庫的創建和升級
* 回傳 true 表示 ContentProvider 初始化成功,回傳 false 表示失敗
*/
override fun onCreate(): Boolean {
return false
}
/*
* 更新 ContentProvider 中已有的資料
*/
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
/*
* 從 ContentProvider 中洗掉資料
*/
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
/*
* 根據傳入的內容 URI 回傳相應的 MIME 型別
*/
override fun getType(uri: Uri): String? {
return null
}
}
可以看到,很多方法里帶有 uri 這個引數,這個引數也正是呼叫 ContentResolver 的增刪改查方法時傳遞過來的,我們需要對傳入的 uri 引數進行決議,從中分析出呼叫方期望訪問的表和資料
我們可以在這個內容 URI 的后面加上一個 id
content://com.example.app.provider/table1/1
表示呼叫方期望訪問的是 com.example.app 這個應用的 table1 表中 id 為 1 的資料
我們還可以使用通配符分別匹配這兩種格式的內容 URI,規則如下
- * 表示匹配任意長度的任意字符
- # 表示匹配任意長度的數字
一個能匹配任意表的內容 URI 格式就可以寫成:
content://com.example.app.provider/*
一個能夠匹配 table1 表中任意一行資料的內容 URI 格式就可以寫成:
content://com.example.app.provider/#
接著,我們再借助 UriMatcher 這個類就可以實作匹配內容 Uri 的功能,該類提供了一個 addURI() 方法,這個方法接收三個引數,可以分別把 authority、path 和一個自定義代碼傳進去,當呼叫 UriMatcher 的 match() 方法時,就可以將一個 Uri 物件傳進去,回傳值是某個能匹配這個 Uri 物件所對應的自定義代碼
class MyProvider : ContentProvider() {
private val table1Dir = 0
private val table1Item = 1
private val table2Dir = 2
private val table2Item = 3
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
init {
uriMatcher.addURI("com.example.app.provider", "table1", table1Dir)
uriMatcher.addURI("com.example.app.provider", "table1/#", table1Item)
uriMatcher.addURI("com.example.app.provider", "table2", table2Dir)
uriMatcher.addURI("com.example.app.provider", "table2/#", table2Item)
}
...
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
when (uriMatcher.match(uri)) {
table1Dir -> {
// 查詢 table1 表中的所有資料
}
table1Item -> {
// 查詢 table1 表中的單條資料
}
table2Dir -> {
// 查詢 table2 表中的所有資料
}
table2Item -> {
// 查詢 table2 表中的單條資料
}
}
}
}
上述代碼只是以 query() 方法為例做了個示范,其實 insert()、update()、delete() 這幾個方法的實作是差不多的
除此以外,還有 getType() 方法,用于獲取 Uri 物件所對應的 MIME 型別,一個內容 URI 所對應的 MIME 字串主要由三個部分組成,Android 對這三個部分做了如下格式規定:
-
必須以 vnd 開頭
-
如果內容 URI 以路徑結尾,則后接 android.cursor.dir/
如果內容 URI 以 id 結尾,則后接 android.cursor.item/
-
最后接上 vnd.<authority>.<path>
繼續完善 MyProvider 的內容,實作 getType() 方法中的邏輯
class MyProvider : ContentProvider() {
...
override fun getType(uri: Uri) = when (uriMatcher.match(uri)) {
table1Dir -> "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"
table1Item -> "vnd.android.cursor.item/vnd.com.example.app.provider.table1"
table2Dir -> "vnd.android.cursor.dir/vnd.com.example.app.provider.table2"
table2Item -> "vnd.android.cursor.item/vnd.com.example.app.provider.table2"
else -> null
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/357144.html
標籤:其他
