You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中ContentProvider与Room结合使用的技术疑问求助

理解ContentProvider中的查询类型代码 & CursorLoader查询实现

我来帮你拆解这两个点,结合Room和ContentProvider的使用逻辑一步步讲清楚:

一、CODE_CHEESE_DIR 和 CODE_CHEESE_ITEM 的核心作用

这两个私有整数常量是UriMatcher的匹配码,用来区分两种查询场景:

  • CODE_CHEESE_DIR:对应批量查询全部奶酪数据的请求
  • CODE_CHEESE_ITEM:对应按ID查询单条奶酪数据的请求

它们的工作逻辑是这样的:

  1. 在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)
    }
    
  2. 当外部调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:02:48