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

Android Firebase聊天应用消息乱序问题排查求助

聊天消息乱序问题排查与解决

我基于Firebase开发了一款Android聊天应用,其余功能均正常,但用户聊天时消息无法按顺序展示,呈现随机排列的情况,图片消息也无法正常排序。不清楚问题成因,以下是相关代码,请帮忙排查并指导解决方法。

问题截图

消息乱序展示

Adapter代码

public class MessageAdapter extends RecyclerView.Adapter {

Context context;
ArrayList<Message> messageArrayList;

int ITEM_SENT = 1;
int ITEM_RECEIVE = 2;

String senderRoom;
String receiverRoom;

public MessageAdapter()
{

}

public MessageAdapter(Context context, ArrayList<Message> messageArrayList, String senderRoom, String receiverRoom) {
    this.context = context;
    this.messageArrayList = messageArrayList;
    this.senderRoom = senderRoom;
    this.receiverRoom = receiverRoom;
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

    if (viewType==ITEM_SENT)
    {
        View view = LayoutInflater.from(context).inflate(R.layout.senderchatlayout, parent, false);
        return new SenderViewHolder(view);
    }else
    {

        View view = LayoutInflater.from(context).inflate(R.layout.receiverchatlayout, parent, false);
        return new RecieverViewHolder(view);
    }
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

    Message message = messageArrayList.get(position);
    if (holder.getClass() == SenderViewHolder.class)
    {

        SenderViewHolder viewHolder = (SenderViewHolder) holder;
        viewHolder.textViewMessage.setText(message.getMessage());
        viewHolder.timeofmessage.setText(message.getCurrenttime());
    }else
    {
        RecieverViewHolder viewHolder = (RecieverViewHolder) holder;
        viewHolder.textViewMessage.setText(message.getMessage());
        viewHolder.timeofmessage.setText(message.getCurrenttime());
    }

}

@Override
public int getItemViewType(int position) {

    Message message = messageArrayList.get(position);
    if (FirebaseAuth.getInstance().getCurrentUser().getUid().equals(message.getSenderId()))
    {
        return ITEM_SENT;
    }else
    {
        return ITEM_RECEIVE;
    }

}

@Override
public int getItemCount() {
    return messageArrayList.size();
}

  public class SenderViewHolder extends RecyclerView.ViewHolder
  {

   TextView textViewMessage;
   TextView timeofmessage;

   public SenderViewHolder(@NonNull View itemView) {
       super(itemView);

       textViewMessage = itemView.findViewById(R.id.senderMessage);
       timeofmessage = itemView.findViewById(R.id.senderTime);
   }
 }

public class RecieverViewHolder extends RecyclerView.ViewHolder
{

    TextView textViewMessage;
    TextView timeofmessage;
    public RecieverViewHolder(@NonNull View itemView) {
        super(itemView);

        textViewMessage = itemView.findViewById(R.id.receiverMessage);
        timeofmessage = itemView.findViewById(R.id.receiverTime);
    }
}
}

ChatActivity消息获取代码

public class SpecificChatActivity extends AppCompatActivity {

ActivitySpecificChatBinding binding;
private FirebaseAuth auth;
FirebaseFirestore firebaseFirestore;
private String enteredMessage;
String mreceiveruid, msenderuid;
String senderroom, receiverroom;
String currenttime;
Calendar calendar;
String conversionId;
ArrayList<Message> messageArrayList;
MessageAdapter adapter;
SimpleDateFormat simpleDateFormat;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ActivitySpecificChatBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());

    auth = FirebaseAuth.getInstance();
    firebaseFirestore = FirebaseFirestore.getInstance();
    calendar = Calendar.getInstance();
    simpleDateFormat = new SimpleDateFormat("hh:mm a");
    setSupportActionBar(binding.specificToolbar);

    messageArrayList = new ArrayList<>();

    String id = UUID.randomUUID().toString();

    Intent intent = getIntent();
    String name = intent.getStringExtra("name");
    String image = intent.getStringExtra("profileImage");

    msenderuid = auth.getUid();
    mreceiveruid = intent.getStringExtra("receiverUid");

    senderroom = msenderuid+mreceiveruid;
    receiverroom = mreceiveruid+msenderuid;


    binding.specificUserName.setText(name);
    if (!image.isEmpty())
    {
        Glide.with(getApplicationContext())
                .load(image)
                .placeholder(R.drawable.userchaticon)
                .into(binding.specificUserImage);

    }


    binding.sendBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            enteredMessage = binding.messageBox.getText().toString();
            if (enteredMessage.isEmpty())
            {
                Toast.makeText(SpecificChatActivity.this, "Entered some message", Toast.LENGTH_SHORT).show();
            }else
            {

                Date date = new Date();
                currenttime = simpleDateFormat.format(calendar.getTime());
                Message message = new Message(enteredMessage, auth.getUid(), mreceiveruid,  date.getTime(), currenttime, name, image);
                firebaseFirestore.collection("Chats")
                        .document(senderroom)
                        .collection("Messages")
                        .document()
                        .set(message).addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {


                        firebaseFirestore.collection("Chats")
                                .document(receiverroom)
                                .collection("Messages")
                                .document()
                                .set(message).addOnCompleteListener(new OnCompleteListener<Void>() {
                            @Override
                            public void onComplete(@NonNull Task<Void> task) {

                            }
                        });
                    }
                });

                binding.messageBox.setText(null);

            }

        }
    });

    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    linearLayoutManager.setStackFromEnd(true);
    binding.specificUserRecycler.setLayoutManager(linearLayoutManager);
    adapter = new MessageAdapter(SpecificChatActivity.this, messageArrayList, senderroom, receiverroom);
    binding.specificUserRecycler.setAdapter(adapter);

    CollectionReference collectionReference = firebaseFirestore.collection("Chats").document(senderroom)
            .collection("Messages");
    collectionReference.addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(@Nullable QuerySnapshot value, @Nullable FirebaseFirestoreException error) {
            messageArrayList.clear();

            for (DocumentSnapshot snapshots : value.getDocuments())
            {
                Message message = snapshots.toObject(Message.class);
                messageArrayList.add(message);
            }
            adapter.notifyDataSetChanged();
        }
    });
    
}
}

问题根源

Firestore默认不会对查询结果排序,当前获取消息时直接遍历value.getDocuments(),而Firestore文档是按生成的随机ID排序的,所以消息会呈现随机排列。

解决步骤

  1. 查询时按时间戳排序
    修改消息查询代码,添加orderBy条件,根据Message实体中的时间戳字段(对应创建Message时传入的date.getTime())升序排列:
CollectionReference collectionReference = firebaseFirestore.collection("Chats")
        .document(senderroom)
        .collection("Messages")
        .orderBy("timestamp", Query.Direction.ASCENDING);

注意:需确保Message类中存储时间戳的字段名为timestamp,如果实际字段名不同(比如time),请替换为对应名称。

  1. 创建Firestore索引(若提示缺失)
    添加排序条件后,如果Logcat中出现索引缺失的错误提示,点击提示中的链接即可自动创建所需的复合索引,Firestore会自动生成正确的索引配置。

  2. 统一时间戳逻辑(可选优化)
    发送消息时,当前用calendar.getTime()生成显示时间,用date.getTime()存储时间戳,可统一为date.getTime()生成时间戳,避免潜在的时间不一致问题:

Date date = new Date();
currenttime = simpleDateFormat.format(date); // 替换calendar.getTime()为date
Message message = new Message(enteredMessage, auth.getUid(), mreceiveruid,  date.getTime(), currenttime, name, image);

额外注意事项

  • 禁止使用字符串格式的时间(如currenttime)进行排序,字符串排序会导致逻辑错误(例如"10:00 AM"会排在"9:00 AM"之前),必须用long类型的时间戳排序。
  • 若后续添加图片消息,只要图片消息的Message实体同样包含正确的时间戳字段,排序逻辑会自动生效,无需额外修改。

内容的提问来源于stack exchange,提问作者Vivek M Fauzdar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:10:28