You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Firebase聊天应用无法接收消息且消息群发问题求助(附MessageAdapter与ChatActivity代码)

解决Firebase聊天应用的两个核心问题

我来帮你分析并修复这两个问题,先看代码里的关键错误:

问题1:无法显示他人消息的核心原因

你的ChatActivity里获取Intent的方式完全错误,你新建了一个空的Intent(),而不是获取启动当前Activity的Intent,导致recieverUid一直是null,生成的房间路径完全不对,自然无法监听到对方发送的消息。

另外,数据更新后RecyclerView没有自动滚动到最新消息,可能也会让你误以为没收到消息。

问题2:消息推送给所有用户的原因

同样是因为recieverUidnull,生成的房间路径格式错误(比如变成null+当前用户UID),如果多个用户的ChatActivity都出现这个错误,可能会导致大家都监听同一个错误路径,或者消息被存到了公共节点下,最终所有用户都收到推送。


修复后的完整代码

1. 修复ChatActivity的关键错误

var firebaseUser: FirebaseUser? = null
private lateinit var messageRecyclerView: RecyclerView
private lateinit var messageBox: EditText
private lateinit var sendButton: ImageButton
private lateinit var messageAdapter: MessageAdapter
private lateinit var messageList: ArrayList<Message>
private lateinit var mRef: DatabaseReference
var recieverRoom: String? = null
var senderRoom: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_chat)

    // 修复:获取启动当前Activity的Intent,而不是新建空Intent
    val intent = this.intent
    val name = intent.getStringExtra("name")
    val recieverUid = intent.getStringExtra("uid")
    val senderUid = FirebaseAuth.getInstance().currentUser?.uid ?: return // 增加空判断,避免后续空指针

    mRef = FirebaseDatabase.getInstance().reference

    // 生成房间路径
    senderRoom = recieverUid + senderUid
    recieverRoom = senderUid + recieverUid

    // 设置RecyclerView
    messageRecyclerView = findViewById(R.id.RecyclerViewChat)
    messageBox = findViewById(R.id.messageBox)
    sendButton = findViewById(R.id.sendButton)

    messageList = ArrayList()
    messageAdapter = MessageAdapter(this, messageList)
    messageRecyclerView.layoutManager = LinearLayoutManager(this)
    messageRecyclerView.adapter = messageAdapter

    // 监听消息变化
    mRef.child("chats").child(senderRoom!!).child("messages")
        .addValueEventListener(object : ValueEventListener{
            override fun onDataChange(snapshot: DataSnapshot) {
                messageList.clear()
                for (postSnapshot in snapshot.children) {
                    val message = postSnapshot.getValue(Message::class.java)
                    message?.let { messageList.add(it) } // 增加空安全判断
                }
                messageAdapter.notifyDataSetChanged()
                // 修复:数据更新后自动滚动到最新消息
                messageRecyclerView.scrollToPosition(messageAdapter.itemCount - 1)
            }

            override fun onCancelled(error: DatabaseError) {
                // 增加错误日志,方便排查问题
                Log.e("ChatActivity", "监听消息失败: ${error.message}")
            }
        })

    sendButton.setOnClickListener {
        val messageText = messageBox.text.toString().trim()
        if (messageText.isNotEmpty()) {
            val messageObject = Message(messageText, senderUid)
            // 同时向两个房间写入消息
            mRef.child("chats").child(senderRoom!!).child("messages").push()
                .setValue(messageObject).addOnSuccessListener {
                    mRef.child("chats").child(recieverRoom!!).child("messages").push()
                        .setValue(messageObject)
                }.addOnFailureListener {
                    Log.e("ChatActivity", "发送消息失败: ${it.message}")
                }
            messageBox.setText("")
        }
    }
}

2. 确保Message类结构正确(关键)

你的Message类需要符合Firebase的序列化要求,必须有空构造函数,字段名要和数据库里的一致:

class Message {
    var message: String? = null
    var senderId: String? = null

    // Firebase序列化必须的空构造函数
    constructor() {}

    // 自定义构造函数
    constructor(message: String, senderId: String?) {
        this.message = message
        this.senderId = senderId
    }
}

3. 优化MessageAdapter的空安全判断(可选)

class MessageAdapter(val context: Context, val messageList: ArrayList<Message>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private val ITEM_RECIEVE = 1
    private val ITEM_SENT = 2

    class SentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val sentMessage = itemView.findViewById<TextView>(R.id.txt_sent_message)
    }

    class RecieveViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val recieveMessage = itemView.findViewById<TextView>(R.id.txt_recieve_message)
    }

    override fun getItemViewType(position: Int): Int {
        val currentMessage = messageList[position]
        val currentUid = FirebaseAuth.getInstance().currentUser?.uid
        // 增加空安全判断,避免空指针
        return if (currentUid != null && currentUid == currentMessage.senderId) {
            ITEM_SENT
        } else {
            ITEM_RECIEVE
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return if (viewType == ITEM_RECIEVE) {
            val view = LayoutInflater.from(context).inflate(R.layout.recieve, parent, false)
            RecieveViewHolder(view)
        } else {
            val view = LayoutInflater.from(context).inflate(R.layout.sent, parent, false)
            SentViewHolder(view)
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val currentMessage = messageList[position]
        when(holder) {
            is SentViewHolder -> holder.sentMessage.text = currentMessage.message
            is RecieveViewHolder -> holder.recieveMessage.text = currentMessage.message
        }
    }

    override fun getItemCount(): Int {
        return messageList.size
    }
}

额外优化建议(可选)

目前的房间路径生成方式(recieverUid + senderUidsenderUid + recieverUid)需要维护两个节点,你可以改成按字典序排序UID生成唯一房间ID,这样只需要一个节点即可:

val sortedUids = listOf(senderUid, recieverUid).sorted()
val roomId = "${sortedUids[0]}_${sortedUids[1]}"
// 后续只需要使用roomId作为路径,不用再维护senderRoom和recieverRoom

这样发送和监听消息只需要操作mRef.child("chats").child(roomId).child("messages"),代码更简洁,也减少了出错概率。

内容的提问来源于stack exchange,提问作者BlackAndDuck

火山引擎 最新活动