如何实现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
相关产品推荐
相关产品推荐

