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

如何在后台线程复制Realm查询结果同时在主线程监听变更?

解决Realm跨线程访问异常并在后台执行copyFromRealm

问题原因

当前代码在主线程的Realm监听器中,直接在后台线程调用results.realm.copyFromRealm(results),而results及其关联的Realm实例属于主线程。Realm对象仅允许在创建它们的线程中访问,因此触发IllegalStateException。

解决方案

由于监听器必须在主线程添加(依赖Looper),我们可以通过以下方式实现后台执行copyFromRealm:

  • 在主线程获取Realm的配置信息
  • 在后台线程使用该配置创建独立的Realm实例
  • 在后台线程重新执行相同查询,获取线程安全的Results对象
  • 完成copyFromRealm和排序后,关闭后台Realm实例
  • 将处理后的列表发送到Flow

修正后的代码

override fun getUsersFlow(projectId: Int?): Flow<List<UserEntity>> {
    return callbackFlow {
        val mainRealm = Realm.getDefaultInstance()
        val realmConfig = mainRealm.configuration // 获取主线程Realm的配置

        val projectBasedQuery = mainRealm.where(UserEntity::class.java).apply {
            projectId?.let { equalTo("projectId", it) }
        }

        var copyJob: Job? = null
        val findAllAsync = projectBasedQuery.findAllAsync()

        findAllAsync.addChangeListener { _ ->
            // 监听器触发时,在后台线程执行复制操作
            copyJob = CoroutineScope(Dispatchers.Default).launch {
                // 后台线程创建独立的Realm实例
                val backgroundRealm = Realm.getInstance(realmConfig)
                try {
                    // 重新执行相同查询,获取后台线程的Results
                    val backgroundResults = backgroundRealm.where(UserEntity::class.java).apply {
                        projectId?.let { equalTo("projectId", it) }
                    }.findAll()
                    // 执行copyFromRealm和排序
                    val items = backgroundRealm.copyFromRealm(backgroundResults)
                    val sortedItems = items.sortedByDescending { it.createdTime }
                    trySend(sortedItems)
                } finally {
                    // 确保后台Realm被关闭
                    if (!backgroundRealm.isClosed) {
                        backgroundRealm.close()
                    }
                }
            }
        }

        awaitClose {
            findAllAsync.removeAllChangeListeners()
            copyJob?.cancel()
            if (!mainRealm.isClosed) {
                mainRealm.close()
            }
        }
    }.flowOn(Dispatchers.Main)
}

关键说明

  • 每个线程必须拥有独立的Realm实例,后台线程通过主线程的配置创建自己的Realm,避免跨线程访问问题
  • 后台线程需要重新执行查询,因为原Results对象属于主线程,无法在后台直接使用
  • 使用try-finally确保后台Realm实例始终被关闭,防止资源泄漏
  • 取消copyJob时,后台协程会被终止,Realm的关闭操作仍会执行(因为在finally块中)

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 15:25:14