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

如何实现Firebase数据库实时更新并解决应用崩溃问题?

问题分析

崩溃的直接原因是addValueEventListener会在数据初始加载和每次变更时触发onDataChange,但你用suspendCoroutine创建的continuation只能被resume一次,第二次调用就会抛出IllegalStateException: Already resumed。同时当前实现只返回了初始加载的StateFlow,无法实时推送数据更新,导致RecyclerView不能自动刷新。

修复方案

改用callbackFlow来实现实时数据流,它支持多次发送数据,完美适配Firebase的实时监听特性。修改后的代码如下:

override fun getAllOnline(): Flow<ResourceState<List<DocModel>>> = callbackFlow {
    val auth = FirebaseAuth.getInstance()
    val userId = auth.currentUser?.uid ?: run {
        trySend(ResourceState.Error("用户未登录"))
        close()
        return@callbackFlow
    }

    val valueEventListener = object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            val docList = snapshot.children.mapNotNull { it.getValue(DocModel::class.java) }.toMutableList()
            val resourceState = if (docList.isNotEmpty()) {
                ResourceState.Success(docList)
            } else {
                ResourceState.Empty()
            }
            trySend(resourceState).isSuccess // 发送最新状态到Flow
        }

        override fun onCancelled(error: DatabaseError) {
            trySend(ResourceState.Error(error.message))
            close(error.toException()) // 出错时关闭Flow
        }
    }

    // 添加实时监听器
    database.child(userId).addValueEventListener(valueEventListener)

    // 协程取消时移除监听器,避免内存泄漏
    awaitClose {
        database.child(userId).removeEventListener(valueEventListener)
    }
}
关键修改点说明
  • 替换suspendCoroutine为callbackFlow:允许多次向Flow发送数据,每次数据变化时都能推送最新状态到UI层。
  • 自动管理监听器生命周期:通过awaitClose在协程取消时移除Firebase监听器,防止内存泄漏。
  • 处理用户未登录场景:提前判断用户UID,避免空指针异常。
  • 用mapNotNull替代强制非空断言!!:更安全地处理可能的空数据。
UI层适配示例

在ViewModel中收集这个Flow,转换为StateFlow供UI观察:

class DocViewModel(private val repository: DocRepository) : ViewModel() {
    val docState: StateFlow<ResourceState<List<DocModel>>> = repository.getAllOnline()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = ResourceState.Loading()
        )
}

在Activity/Fragment中观察StateFlow并更新RecyclerView:

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.docState.collect { state ->
            when (state) {
                is ResourceState.Success -> {
                    // 更新RecyclerView适配器数据
                    adapter.submitList(state.data)
                }
                is ResourceState.Empty -> {
                    // 显示空状态
                }
                is ResourceState.Error -> {
                    // 显示错误提示
                }
                is ResourceState.Loading -> {
                    // 显示加载中
                }
            }
        }
    }
}

这样修改后,无论是Web端删除数据还是App内滑动删除,Firebase的实时监听器都会触发onDataChange,Flow会自动推送最新数据到UI层,RecyclerView会自动刷新,同时解决了Already resumed的崩溃问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 08:05:33