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

ViewModel获取Firebase用户列表为空 方法执行时序异常问题

问题根因

你遇到的列表为空问题本质是异步API时序逻辑错误,和ViewModel、Repository的分层结构本身无关:

  • 你对执行时序的判断存在误解:readUsers()方法本身在ViewModel初始化阶段就同步执行完成了,只是Firebase的addValueEventListener是异步非阻塞API,调用时只会完成监听器注册,不会立刻返回网络请求结果,onDataChange回调会在Firebase拉取数据完成后才排队执行,这个时机晚于你在Fragment中注册Observer的时机。
  • 现有代码在注册监听器后立刻返回了当时还未填充数据的空ArrayList对象,并把这个空列表赋值给LiveData。后续onDataChange执行时虽然往同一个ArrayList对象里添加了用户数据,但你没有主动通知LiveData数据发生变更,UI层的观察者完全感知不到数据变化,自然不会刷新列表。
  • 你调试时看到usersList非空,是因为调试时机晚于异步回调执行,回调已经往这个列表对象里填充了数据,但不代表UI层收到了数据更新通知。
  • 原有代码还存在额外风险:全局持有可变列表、滥用非空断言!!、每次数据回调都重建RecyclerView Adapter,会带来内存泄漏、Crash、列表状态丢失等问题。
修正方案

按分层逻辑逐块调整代码即可:

1. 修正Repository层,不要同步返回异步结果

移除Repository中全局持有的可变列表,改用LiveData承载异步返回的数据,在数据拉取完成后主动推送结果:

class UserRepository {
    fun readUsers(): LiveData<List<User>> {
        val resultLiveData = MutableLiveData<List<User>>()
        val currentUser = FirebaseAuth.getInstance().currentUser
        val dbRef = FirebaseDatabase.getInstance().getReference("Users")

        dbRef.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val userList = mutableListOf<User>()
                for (data in dataSnapshot.children) {
                    val user = data.getValue(User::class.java)
                    // 过滤掉当前登录用户,同时避免空值风险
                    if (user != null && user.id != currentUser?.uid) {
                        userList.add(user)
                    }
                }
                // 数据就绪后推送结果,通知所有观察者
                resultLiveData.postValue(userList)
            }

            override fun onCancelled(error: DatabaseError) {
                // 按业务需求处理错误,这里示例返回空列表
                resultLiveData.postValue(emptyList())
            }
        })
        return resultLiveData
    }
}

2. 修正ViewModel层,减少冗余可空声明

对外暴露不可变LiveData,避免外部篡改数据状态,移除没必要的可空类型声明和多余的初始化块:

class UserViewModel(application: Application) : AndroidViewModel(application) {
    private val userRepository = UserRepository()
    val allUsers: LiveData<List<User>> = userRepository.readUsers()
}

3. 修正Fragment层逻辑

废弃已经被官方标记为过时的onActivityCreated,在onViewCreated中完成UI初始化和数据观察,不要每次数据更新都重建Adapter:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // 先初始化RecyclerView和空Adapter
    userAdapter = UserAdapter(requireContext(), emptyList(), false)
    recyclerView.adapter = userAdapter

    // 获取ViewModel实例并观察数据
    val userViewModel = ViewModelProvider(this)[UserViewModel::class.java]
    userViewModel.allUsers.observe(viewLifecycleOwner) { newUserList ->
        // 数据更新时仅刷新Adapter的数据集,不要重建Adapter
        // 如果用的是ListAdapter,直接调用submitList(newUserList)即可
        userAdapter.updateData(newUserList)
    }
}

额外提示:如果你的UserAdapter继承自ListAdapter,不需要自己写updateData方法,直接调用内置的submitList方法就能自动完成数据差分刷新,性能更好也不会丢失列表滚动位置。如果是普通的RecyclerView.Adapter,自行实现一个替换数据集+触发通知的方法即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:06:21