如何批量查询Firestore用户ID数组,解决RecyclerView单条数据加载问题?
问题描述
我在社交应用中同时使用Realtime Database和Firestore:Realtime Database存储用户详情、实现消息功能;Firestore有一个状态视频集合,存储视频详情及发布者的用户ID。获取状态视频时,我会提取这些用户ID存入数组,打算通过每个ID从Realtime Database获取用户详情,但每次仅在onBindView方法中发起一次请求,只有一个视图能收到用户名和头像数据。怎么根据数据集数量发起对应请求,确保数组中所有ID的用户数据都能获取到?
主活动代码
private void getStatusVid() { database.collection(Constants.KEY_STATUS) .limit(25) .get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull @NotNull Task<QuerySnapshot> task) { if(task.isSuccessful()&& task.getResult()!=null){ for (QueryDocumentSnapshot queryDocumentSnapshot:task.getResult()){ mVidsList.add(new StatusVids(queryDocumentSnapshot.getString(Constants.KEY_USER_ID),queryDocumentSnapshot.getString(Constants.KEY_COVER),queryDocumentSnapshot.getString(Constants.KEY_NAME),queryDocumentSnapshot.getString(Constants.KEY_TITLE),"")); } if(mVidsList.size()>0){ mRecyclerVids.setAdapter(new StatusVidsAdapter(mVidsList,getActivity())); } } } }); }
Adapter的onBindView代码
mRef.child(Constants.KEY_COLLECTION_USERS).child(mList.get(position).userId).addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull @NotNull DataSnapshot snapshot) { for(DataSnapshot snappy: snapshot.getChildren()){} if(snapshot.exists()){ final String pictureUrl=snapshot.child("profile").getValue(String.class); Glide.with(context) .load(pictureUrl) .circleCrop() .into(holder.profile); } } @Override public void onCancelled(@NonNull @NotNull DatabaseError error) { Toast.makeText(context,error.getMessage(),Toast.LENGTH_SHORT).show(); } });
解决方案
1. 问题根源
onBindView是RecyclerView复用视图时触发的方法,你使用addValueEventListener(实时监听)却未移除监听,会导致旧监听与新视图绑定冲突;同时RecyclerView的复用机制下,可能只有最后一次请求的结果覆盖到视图上。另外,对于仅需单次获取的用户数据,实时监听属于资源浪费。
2. 修复方案
(1)改用单次数据监听
将addValueEventListener替换为addListenerForSingleValueEvent,确保每次onBindView仅发起一次单次请求,避免不必要的实时更新:
mRef.child(Constants.KEY_COLLECTION_USERS).child(mList.get(position).userId) .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { if(snapshot.exists()){ String pictureUrl = snapshot.child("profile").getValue(String.class); Glide.with(context) .load(pictureUrl) .circleCrop() .into(holder.profile); } } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(context, error.getMessage(), Toast.LENGTH_SHORT).show(); } });
(2)解决视图复用的显示混乱
在onBindView开头给头像设置默认占位图,防止复用旧视图的残留数据:
// 设置本地默认头像 holder.profile.setImageResource(R.drawable.default_profile); // 再发起请求加载真实头像 mRef.child(Constants.KEY_COLLECTION_USERS).child(mList.get(position).userId) .addListenerForSingleValueEvent(/* 上述监听逻辑 */);
(3)优化:预加载所有用户数据(可选)
如果想减少onBindView中的网络请求次数,可以在获取视频列表时批量预加载所有关联用户的详情:
private void getStatusVid() { database.collection(Constants.KEY_STATUS) .limit(25) .get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull Task<QuerySnapshot> task) { if(task.isSuccessful()&& task.getResult()!=null){ List<String> userIds = new ArrayList<>(); for (QueryDocumentSnapshot queryDocumentSnapshot:task.getResult()){ String userId = queryDocumentSnapshot.getString(Constants.KEY_USER_ID); mVidsList.add(new StatusVids(userId, queryDocumentSnapshot.getString(Constants.KEY_COVER), queryDocumentSnapshot.getString(Constants.KEY_NAME), queryDocumentSnapshot.getString(Constants.KEY_TITLE), "")); userIds.add(userId); } // 批量获取用户头像数据 if(!userIds.isEmpty()){ mRef.child(Constants.KEY_COLLECTION_USERS) .orderByKey() .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { Map<String, String> userProfileMap = new HashMap<>(); for(DataSnapshot userSnap : snapshot.getChildren()){ if(userIds.contains(userSnap.getKey())){ String profileUrl = userSnap.child("profile").getValue(String.class); userProfileMap.put(userSnap.getKey(), profileUrl); } } // 更新视频列表中的头像数据 for(StatusVids vid : mVidsList){ vid.setProfileUrl(userProfileMap.get(vid.userId)); } // 设置Adapter if(mVidsList.size()>0){ mRecyclerVids.setAdapter(new StatusVidsAdapter(mVidsList,getActivity())); } } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_SHORT).show(); } }); } else { if(mVidsList.size()>0){ mRecyclerVids.setAdapter(new StatusVidsAdapter(mVidsList,getActivity())); } } } } }); }
此时onBindView只需直接从StatusVids对象读取头像URL加载即可,无需再发起网络请求,性能更优。
3. 注意事项
- 确保
StatusVids类添加profileUrl字段及对应的setter和getter方法。 - 批量查询时使用
orderByKey()可优化Realtime Database的查询性能。
内容的提问来源于stack exchange,提问作者mike
相关产品推荐
相关产品推荐

