Android中ContentProvider与Room结合使用的技术疑问求助
理解ContentProvider中的查询类型代码 & CursorLoader查询实现
我来帮你拆解这两个点,结合Room和ContentProvider的使用逻辑一步步讲清楚:
一、CODE_CHEESE_DIR 和 CODE_CHEESE_ITEM 的核心作用
这两个私有整数常量是UriMatcher的匹配码,用来区分两种查询场景:
CODE_CHEESE_DIR:对应批量查询全部奶酪数据的请求CODE_CHEESE_ITEM:对应按ID查询单条奶酪数据的请求
它们的工作逻辑是这样的:
- 在ContentProvider初始化时,你需要用
UriMatcher把不同格式的Uri和这两个码绑定:private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { // 匹配"content://你的AUTHORITY/cheeses",对应批量查询 addURI(YOUR_AUTHORITY, "cheeses", CODE_CHEESE_DIR) // 匹配"content://你的AUTHORITY/cheeses/123"(#代表数字ID),对应单条查询 addURI(YOUR_AUTHORITY, "cheeses/#", CODE_CHEESE_ITEM) } - 当外部调用ContentProvider的
query()方法时,会先通过uriMatcher.match(uri)拿到对应的匹配码,再执行对应逻辑:override fun query( uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String? ): Cursor? { return when (uriMatcher.match(uri)) { CODE_CHEESE_DIR -> { // 调用Room Dao的批量查询方法,返回Cursor yourRoomDb.cheeseDao().getAllCheeses() } CODE_CHEESE_ITEM -> { // 从Uri中解析出ID,调用单条查询方法 val cheeseId = ContentUris.parseId(uri) yourRoomDb.cheeseDao().getCheeseById(cheeseId) } else -> throw IllegalArgumentException("无法识别的Uri: $uri") } }
二、用CursorLoader指定查询条件的实现方法
CursorLoader是用来异步加载ContentProvider数据的工具,指定查询条件主要通过构造参数或动态设置筛选规则,分三种常见场景:
1. 加载全部数据(无筛选)
直接传入对应CODE_CHEESE_DIR的Uri即可:
val allCheesesLoader = CursorLoader( context, CheeseContentProvider.CONTENT_URI, // 批量查询的基础Uri arrayOf(Cheese.COLUMN_ID, Cheese.COLUMN_NAME), // 要查询的列(projection) null, // selection:查询条件,null表示无筛选 null, // selectionArgs:条件参数,和selection的?对应 "${Cheese.COLUMN_NAME} ASC" // 排序规则 )
2. 按自定义条件筛选(比如按名称模糊匹配)
通过selection和selectionArgs设置筛选规则,注意用?占位符避免SQL注入:
// 查询名称包含"cheddar"的奶酪 val selection = "${Cheese.COLUMN_NAME} LIKE ?" val selectionArgs = arrayOf("%cheddar%") val filteredLoader = CursorLoader( context, CheeseContentProvider.CONTENT_URI, arrayOf(Cheese.COLUMN_ID, Cheese.COLUMN_NAME), selection, selectionArgs, "${Cheese.COLUMN_NAME} ASC" )
3. 按ID查询单条数据
构造对应CODE_CHEESE_ITEM的Uri,不需要额外设置selection:
val targetCheeseId = 123 // 给基础Uri追加ID,生成单条查询的Uri val itemUri = ContentUris.withAppendedId(CheeseContentProvider.CONTENT_URI, targetCheeseId) val singleCheeseLoader = CursorLoader( context, itemUri, arrayOf(Cheese.COLUMN_ID, Cheese.COLUMN_NAME), null, null, null )
4. 监听Loader的加载结果
在Activity/Fragment中,通过LoaderManager注册Loader并处理返回的Cursor:
private const val CHEESE_LOADER_ID = 1 // 在onCreate中初始化Loader supportLoaderManager.initLoader(CHEESE_LOADER_ID, null, object : LoaderManager.LoaderCallbacks<Cursor> { override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { // 返回上面定义的任意一个CursorLoader return filteredLoader } override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) { // 处理加载完成的Cursor数据,比如绑定到RecyclerView data?.let { cursor -> while (cursor.moveToNext()) { val cheeseName = cursor.getString(cursor.getColumnIndexOrThrow(Cheese.COLUMN_NAME)) // 处理数据... } } } override fun onLoaderReset(loader: Loader<Cursor>) { // 重置数据,比如清空Adapter } })
内容的提问来源于stack exchange,提问作者ptrvck
相关产品推荐
相关产品推荐

