如何在后台线程复制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
相关产品推荐
相关产品推荐

