无法获取Firebase RealtimeDatabase用户列表,RecyclerView无数据如何解决
问题原因与解决方案
核心问题
Firebase Realtime Database的数据查询是异步操作,addListenerForSingleValueEvent绑定的回调不会阻塞当前线程执行。你调用fetchUsers方法时,网络请求还在执行,回调逻辑尚未触发,方法就直接将初始化的空userList返回了。后续回调执行时数据才被写入列表,但此时RecyclerView的适配器已经用空列表完成初始化,没有收到数据更新的通知,所以界面不会显示加载到的用户数据。
解决方案1:回调完成后刷新适配器(无额外依赖)
1. 修改适配器代码
将原来私有的updateList方法改为公开,同时在更新数据后调用notifyDataSetChanged通知RecyclerView刷新:
class UsersAdpater(val context : Context, val gotData : ArrayList<UserInfo>) : RecyclerView.Adapter<UsersAdpater.UserViewHolder>() { private var data : ArrayList<UserInfo> = gotData // 其他原有代码保持不变 // 修改为公开方法,增加刷新通知 fun updateList(userList : ArrayList<UserInfo>) { data = userList notifyDataSetChanged() } // 其他原有代码保持不变 }
2. 修改Activity代码
提前初始化适配器,拿到异步返回的用户数据后再调用适配器的更新方法:
class NewMessageActivity : AppCompatActivity() { private val TAG = "NewMessageActivityTag/////////////////////////////" private lateinit var binding : ActivityNewMessageBinding private lateinit var realtimeDatabase : FirebaseDatabase // 新增全局适配器、用户列表变量 private lateinit var usersAdapter: UsersAdpater private val userList = ArrayList<UserInfo>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityNewMessageBinding.inflate(layoutInflater) setContentView(binding.root) supportActionBar?.title = "New Message" realtimeDatabase = FirebaseDatabase.getInstance("https://firechat-931d2-default-rtdb.asia-southeast1.firebasedatabase.app/") // 提前初始化适配器,传入空列表 usersAdapter = UsersAdpater(this,userList) binding.rvNewMessageActivity.adapter = usersAdapter binding.rvNewMessageActivity.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false) // 触发数据拉取 fetchUsers() } // 修改为无返回值方法,数据加载完成后直接更新适配器 private fun fetchUsers() { val ref = realtimeDatabase.getReference("/users") Log.d(TAG,"pidsnigfngadsnfpoin") ref.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { userList.clear() if(snapshot.exists()) { Log.d(TAG,"$snapshot") for(child in snapshot.children){ val tempchild = child.getValue(UserInfo::class.java) Log.d(TAG,"$tempchild") userList.add(tempchild ?: UserInfo("default_UID","default_email","default_name","default_picurl")) } } // 通知适配器刷新数据 usersAdapter.updateList(userList) } override fun onCancelled(error: DatabaseError) { Log.d(TAG,"$error ${error.message}") } }) } }
解决方案2:协程实现(可选)
如果你偏好协程写法,可以引入Firebase官方Ktx扩展,将回调式API转换为挂起函数:
- 先引入依赖:
dependencies { implementation 'com.google.firebase:firebase-database-ktx:20.2.2' }
- 修改
fetchUsers为挂起函数:
private suspend fun fetchUsers() : ArrayList<UserInfo> { val ref = realtimeDatabase.getReference("/users") val userList = ArrayList<UserInfo>() // 挂起等待请求返回结果 val snapshot = ref.get().await() if(snapshot.exists()) { for(child in snapshot.children){ val tempchild = child.getValue(UserInfo::class.java) userList.add(tempchild ?: UserInfo("default_UID","default_email","default_name","default_picurl")) } } return userList }
- 在
onCreate中用生命周期协程域调用:
override fun onCreate(savedInstanceState: Bundle?) { // 原有初始化逻辑不变 lifecycleScope.launch { val users = fetchUsers() usersAdapter.updateList(users) } }
内容的提问来源于stack exchange,提问作者Brahat Singh
相关产品推荐
相关产品推荐

