如何在Firestore的onComplete方法中对ArrayList进行排序?
解决Firestore异步查询导致消息列表乱序的问题
问题根源
你遇到的问题核心是Firestore的get()请求是异步执行的,循环中发起的多个用户信息请求的完成顺序不固定,导致直接往ArrayList里add数据时,顺序被打乱——先完成的请求会先把数据加入列表,不管它原本在排序后的消息列表里的位置。
解决方案:绑定消息与用户信息的对应关系
不要用多个独立的ArrayList分别存储消息、时间戳、用户信息,而是创建一个自定义模型类,把每条消息的所有相关数据封装在一起,同时保留原消息列表的排序顺序。
步骤1:定义消息数据模型类
public class MessageInfo { private String message; private long timestamp; // 可根据你实际使用的日期类型调整 private String senderName; private String senderId; // 构造方法 public MessageInfo(String message, long timestamp, String senderId) { this.message = message; this.timestamp = timestamp; this.senderId = senderId; } // setter & getter public void setSenderName(String senderName) { this.senderName = senderName; } public String getMessage() { return message; } public long getTimestamp() { return timestamp; } public String getSenderName() { return senderName; } public String getSenderId() { return senderId; } }
步骤2:修改查询逻辑,保留排序顺序
先把排序后的消息数据存入有序列表,再针对每条消息发起用户信息查询,将结果填充到对应位置的模型对象中:
firebaseFirestore.collection(threads).orderBy(timestamp).addSnapshotListener(new EventListener<QuerySnapshot>() { @Override public void onEvent(@Nullable QuerySnapshot value, @Nullable FirebaseFirestoreException error) { if (value == null) return; int totalMessages = value.size(); List<MessageInfo> messageList = new ArrayList<>(totalMessages); int completedCount = 0; // 按排序顺序初始化消息列表,填充已知数据 for (int i = 0; i < totalMessages; i++) { QueryDocumentSnapshot document = value.getDocuments().get(i); String message = document.getString("message"); long timestamp = document.getLong("timestamp"); // 按实际字段类型调整 String senderId = document.getString("senderId"); messageList.add(new MessageInfo(message, timestamp, senderId)); } // 循环发起用户信息查询,填充到对应位置的模型中 for (int i = 0; i < totalMessages; i++) { int finalIndex = i; MessageInfo currentMsg = messageList.get(i); firebaseFirestore.collection("users").document(currentMsg.getSenderId()).get() .addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() { @Override public void onComplete(@NonNull Task<DocumentSnapshot> task) { if (task.isSuccessful() && task.getResult() != null) { String senderName = task.getResult().getString("firstName"); currentMsg.setSenderName(senderName); } // 所有请求完成后更新RecyclerView if (++completedCount == totalMessages) { recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); adapter = new AdapterRecyclerViewThreads(getActivity(), titleThreads, ThreadsId, extractSenderNames(messageList), extractUserImages(messageList), extractTimestamps(messageList), getAnswer); recyclerView.setAdapter(adapter); } } }); } } }); // 辅助方法:从MessageInfo列表中提取适配器需要的字段 private List<String> extractSenderNames(List<MessageInfo> messageList) { List<String> names = new ArrayList<>(); for (MessageInfo msg : messageList) { names.add(msg.getSenderName()); } return names; } private List<Long> extractTimestamps(List<MessageInfo> messageList) { List<Long> timestamps = new ArrayList<>(); for (MessageInfo msg : messageList) { timestamps.add(msg.getTimestamp()); } return timestamps; } // 同理实现extractUserImages等方法
为什么这个方法有效
- 首先按
orderBy(timestamp)的顺序初始化了messageList,保证列表的基础顺序是正确的。 - 每个用户信息查询完成后,会把数据填充到
messageList中对应的位置,不会改变列表的整体顺序。 - 只有当所有异步请求都完成后,才会把数据传递给适配器,此时所有消息的用户信息都已填充完毕,且顺序和最初排序的消息列表一致。
额外优化建议
如果用户信息不会频繁变更,可以考虑本地缓存用户信息,避免每次加载消息都重复查询用户集合,提升性能同时减少Firestore读取次数。
内容的提问来源于stack exchange,提问作者Marcel Sanchez
相关产品推荐
相关产品推荐

