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
相关产品推荐
相关产品推荐

