Android ContentProvider中使用 room 并支持API 30(android 11)
Posted 匆忙拥挤repeat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ContentProvider中使用 room 并支持API 30(android 11)相关的知识,希望对你有一定的参考价值。
文章目录
前言
ContentProvider
这个东西,印象中,线上项目中,基本就没用过;前阵子,突然想,写个demo玩一下,后来发现room
不知道怎么结合到里面。再查了些资料,搞定了。
这个示例,断断续续地写了两三个周末了,上周完成了添加和条件查询操作;今天把模糊查询、删除、更新操作都完成了。
定义一个ContentProvider的准备工作
定义authority
authority 将用于 manifest 中注册 <provider>
、数据操作的Uri匹配规则,以及 ContentResolver
的增删改查方法 中的 uri
定义一个数据表的相关操作的 Uri 匹配规则
使用 UriMatcher
类;
matcher.addURI(String authority, String path, int code)
room 数据库相关
从 room
数据库中,可以获得 AppDatabase
实例;
从AppDatabase#getOpenHelper()
获取 SupportSQLiteOpenHelper
实例;
从SupportSQLiteOpenHelper#getReadableDatabase()
获取 只读的 SupportSQLiteDatabase
实例;
从SupportSQLiteOpenHelper#getWritableDatabase()
获取 只写的 SupportSQLiteDatabase
实例
示例
manifest 定义
<queries>
<!-- API 30后,调用方,需要在<queries>中定义 -->
<provider android:authorities="com.stone.cper" />
</queries>
<application>
<provider
android:authorities="com.stone.cper"
android:name=".ui.contentp.MyContentProvider"
android:exported="true" />
</application>
MyContentProvider 实现
import android.content.ContentProvider
import android.content.ContentUris
import android.content.ContentValues
import android.content.UriMatcher
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uri
import androidx.sqlite.db.SupportSQLiteQueryBuilder
import com.stone.stoneviewskt.ui.room.appDatabase
import com.stone.stoneviewskt.util.logi
/**
* desc : ROOM + ContentProvider 示例
* address 相关的增、删、改、查(模糊查)示例 都有了
* 示例中关于 User表、User相关的Uri 等示例不全
* author : stone
* email : aa86799@163.com
* time : 2022/12/13 14:33
*/
class MyContentProvider : ContentProvider()
/*
* contentUri = "content://com.stone.cper/..."
* 调用端:contentResolver.insert(CONTENT_URI, contentValues)
*/
private val matcher by lazy UriMatcher(UriMatcher.NO_MATCH)
companion object
const val authority = "com.stone.cper" // 用于 contentUri, 用于 manifest
const val contentUriStr = "content://$authority"
private const val FUNC_ADDRESS_ALL = 10
private const val FUNC_ADDRESS_WHICH = 11
private const val FUNC_ADDRESS_ADD = 12
private const val FUNC_ADDRESS_DEL = 13
private const val FUNC_ADDRESS_UPDATE = 14
private const val FUNC_USER_DATA_ALL = 20
private const val FUNC_USER_DATA_WHICH = 21
private const val FUNC_USER_DATA_ADD = 22
private const val FUNC_USER_DATA_DEL = 23
private const val FUNC_USER_DATA_UPDATE = 24
private const val FUNC_USER_DATA = 25
private const val TABLE_ADDRESS = "address_data"
private const val TABLE_USER = "UserData"
override fun onCreate(): Boolean
logi("MyContentProvider onCreate") // 初始化在 application的 attachBaseContext()和 onCreate()之间
// matcher.addURI 建立对应关系
matcher.addURI(authority, "address/all", FUNC_ADDRESS_ALL)
matcher.addURI(authority, "address/item/add", FUNC_ADDRESS_ADD)
matcher.addURI(authority, "address/item/del", FUNC_ADDRESS_DEL)
matcher.addURI(authority, "address/item/update", FUNC_ADDRESS_UPDATE)
matcher.addURI(authority, "address/item/#", FUNC_ADDRESS_WHICH) // 这里的#代表任意数字,本示例仅在查询使用,查询第几行数据
matcher.addURI(authority, "address/*", FUNC_ADDRESS_ADD) // * 则代表匹配任意长度的任意字符,一般没啥实际意义
matcher.addURI(authority, "user/all", FUNC_USER_DATA_ALL)
matcher.addURI(authority, "user/item/#", FUNC_USER_DATA_WHICH) // 这里的#代表任意数字
matcher.addURI(authority, "user/item/add", FUNC_USER_DATA_ADD)
matcher.addURI(authority, "user/item/del", FUNC_USER_DATA_DEL)
matcher.addURI(authority, "user/item/update", FUNC_USER_DATA_UPDATE)
matcher.addURI(authority, "user/*", FUNC_USER_DATA) // * 则代表匹配任意长度的任意字符,一般没啥实际意义
return true
/*
* 对于单个记录,返回的MIME类型应该以 vnd.android.cursor.item/ 为首的字符串
* 对于多个记录,返回 vnd.android.cursor.dir/ 为首的字符串
*
* return MIME类型字符串
*/
override fun getType(uri: Uri): String?
val name = when (getTableName(uri))
TABLE_ADDRESS -> "address"
TABLE_USER -> "user"
else -> null
return if (uri.path?.contains("/all") == true)
"vnd.android.cursor.dir/$name"
else if (uri.path?.contains("/item") == true)
"vnd.android.cursor.item/$name"
else null
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor?
val flag = when (matcher.match(uri))
FUNC_ADDRESS_ALL, FUNC_ADDRESS_WHICH -> true
FUNC_USER_DATA_ALL, FUNC_USER_DATA_WHICH -> true
else -> false
if (!flag) return null // 不符合uri规则就退出;否则 若是一个没有预定义的 uri,后续操作会引发错误
val table = getTableName(uri)
return appDatabase.openHelper.readableDatabase.query(
SupportSQLiteQueryBuilder.builder(table)
.selection(selection, selectionArgs)
.columns(projection)
.orderBy(sortOrder)
.create()
)
// ContentUris.parseId(uri) // 可以获取到 uri 路径 最后 的数字,如 content://.../../9 获取到数字 9
override fun insert(uri: Uri, values: ContentValues?): Uri? // values 中的 key 为表的 列名
val flag = when (matcher.match(uri))
FUNC_ADDRESS_ADD -> true
FUNC_USER_DATA_ADD -> true
else -> false
if (!flag) return null // 不符合uri规则就退出;否则 若是一个没有预定义的 uri,后续操作会引发错误
val rowId = appDatabase.openHelper.writableDatabase.insert(getTableName(uri), SQLiteDatabase.CONFLICT_REPLACE, values)
return ContentUris.withAppendedId(uri, rowId)
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int
val flag = when (matcher.match(uri))
FUNC_ADDRESS_DEL -> true
FUNC_USER_DATA_DEL -> true
else -> false
if (!flag) return 0 // 不符合uri规则就退出;否则 若是一个没有预定义的 uri,后续操作会引发错误
return appDatabase.openHelper.writableDatabase.delete(getTableName(uri), selection, selectionArgs)
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int
val flag = when (matcher.match(uri))
FUNC_ADDRESS_UPDATE -> true
FUNC_USER_DATA_UPDATE -> true
else -> false
if (!flag) return 0 // 不符合uri规则就退出;否则 若是一个没有预定义的 uri,后续操作会引发错误
return appDatabase.openHelper.writableDatabase.update(getTableName(uri), SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs)
// 根据 uri,匹配出对应的数据表
private fun getTableName(uri: Uri): String?
return when (matcher.match(uri))
FUNC_ADDRESS_ALL, FUNC_ADDRESS_ADD, FUNC_ADDRESS_DEL, FUNC_ADDRESS_WHICH, FUNC_ADDRESS_UPDATE -> TABLE_ADDRESS
FUNC_USER_DATA_WHICH, FUNC_USER_DATA -> TABLE_USER
else -> null
insert() 返回值是一个Uri对象。文档描述是,表示一个新插入项的 Uri。
ContentUris.withAppendedId(uri, rowId)
是在uri的尾部附加 “/rowId” 。而 uri 明明表示的是.../add
动作。后来尝试了ContentUris.withAppendedId("$contentUriStr/address/item".toUri(), rowId)
,实际就是形如content://com.stone.cper/address/item/2011193145
,发现也没问题。最后尝试了直接返回null,insert()和其它操作也是正常的。 … 这… 说明,sdk实现中,并没有强制什么规则?!
触发调用
使用ContentResolver
的增、删、改、查方法,需要传一个uri,以符合 MyContentProvider
中定义的规则
关于 address 相关的 uri 操作,在fragment中的实现:
private fun addressAll()
mAdapter.removeAll()
val projection = arrayOf("*")// 如果为 null,在query 中,会查出所有列
// val projection = null // 如果为 null,在query 中,会查出所有列
val selection = null
val selectionArgs = null
val order = "id asc"
addressQuery("$MyContentProvider.contentUriStr/address/all".toUri(), projection, selection, selectionArgs, order)
private fun addressWhere()
mAdapter.removeAll()
val projection = arrayOf("add_name", "phone", "address", "id")// 如果为 null,在query 中,会查出所有列
val selection = "add_name=?" // 相当于 sql where 子句,不含 where 本身
val selectionArgs = arrayOf("stone")
val order = "id desc"
addressQuery("$MyContentProvider.contentUriStr/address/all".toUri(), projection, selection, selectionArgs, order)
// 模糊查询 like 和 精确查询 = ,相结合
private fun addressFuzzyAll()
mAdapter.removeAll()
val projection = arrayOf("add_name", "phone", "address", "id")// 如果为 null,在query 中,会查出所有列
val selection = "add_name like '%stone%' and phone=?" // 相当于 sql where 子句,不含 where 本身。like 后面的值直接写在这,不能写到 selectionArgs 数组中。
val selectionArgs = arrayOf("phone-557")
val order = "id desc"
addressQuery("$MyContentProvider.contentUriStr/address/all".toUri(), projection, selection, selectionArgs, order)
// 查询一条记录
private fun addressItem()
mAdapter.removeAll()
val row = 2
val projection = arrayOf("*")
val selection = "rowid=?" // 相当于 sql where 子句,不含 where 本身。like 后面的值直接写在这,不能写到 selectionArgs 数组中。
val selectionArgs = arrayOf(row.toString())
val order = "id desc"
addressQuery("$MyContentProvider.contentUriStr/address/item/$row".toUri(), projection, selection, selectionArgs, order)
private fun addressQuery(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, order: String?)
val resolver = requireActivity().contentResolver
resolver.query(uri, projection, selection, selectionArgs, order)
?.use cursor ->
cursor.moveToFirst()
repeat(cursor.count) // 这里用repeat来代替foreach,是因为循环中的 it 指代对象 在后续用不到
val name = cursor.getString(cursor.getColumnIndex("add_name"))
val phone = cursor.getString(cursor.getColumnIndex("phone"))
val address = cursor.getString(cursor.getColumnIndex("address"))
val id = cursor.getString(cursor.getColumnIndex("id"))
logi("$id $name $phone $address")
mAdapter.addItem("$id $name $phone $address")
cursor.moveToNext()
private fun addressAdd()
val random = Random.nextInt(0, 1000)
val values = ContentValues()
// values.put("add_name", "add_name-$random")
values.put("add_name", "stone-$random")
values.put("phone", "phone-$random")
values.put("address", "address-$random")
val resolver = requireActivity().contentResolver
resolver.insert(Uri.parse("$MyContentProvider.contentUriStr/address/item/add"), values)
private fun addressDelete()
val where = "add_name=?"
val selectionArgs = arrayOf("add_name-580")
val resolver = requireActivity().contentResolver
resolver.delete("$MyContentProvider.contentUriStr/address/item/del".toUri(), where, selectionArgs)
addressAll()
private fun addressUpdate()
val where = "add_name=?"
val selectionArgs = arrayOf("add_name-882")
val values = ContentValues()
values.put("phone", "phone-1998")
val resolver = requireActivity().contentResolver
resolver.update("$MyContentProvider.contentUriStr/address/item/update".toUri(), values, where, selectionArgs)
addressAll()
以上是关于Android ContentProvider中使用 room 并支持API 30(android 11)的主要内容,如果未能解决你的问题,请参考以下文章