如何在Kotlin中为Firebase实时数据库按用户ID构建会话列表
问题:基于Kotlin和Firebase Realtime Database实现会话数据结构
需求与目标数据结构
我正在开发一个基于Kotlin和Firebase Realtime Database的Android项目,需要构建如下数据结构:每个用户节点下包含与其他用户的会话列表,每个会话存储双方交互的最新消息。
目标数据结构示例:
{ "403": { "conversations": [ { "id": "conversation_403_407_2024-05-08_18:31:38", "latest_message": { "date": "2024-05-08 18:32:43", "isRead": false, "message": "Jhbhjbhbckskjcbdsljkcnjldsvljdsjlcdskjcvbjkdsbvkjdvjlwvjl" }, "name": "djFbtu315", "otherUserId": 407 } ] }, "407": { "conversations": [ { "id": "conversation_403_407_2024-05-08_18:31:38", "latest_message": { "date": "2024-05-08 18:32:43", "isRead": true, "message": "Jhbhjbhbckskjcbdsljkcnjldsvljdsjlcdskjcvbjkdsbvkjdvjlwvjl" }, "name": "my basic profile", "otherUserId": 403 }, { "id": "conversation_408_407_2024-05-08_19:31:02", "latest_message": { "date": "2024-05-08 19:31:02", "isRead": true, "message": "22" }, "name": "Muhammad", "otherUserId": 408 }, { "id": "conversation_507_407_2024-05-08_19:38:47", "latest_message": { "date": "2024-05-08 19:39:15", "isRead": true, "message": "22" }, "name": "Manuel Crandall", "otherUserId": 507 } ] } }
当前问题
无法正确将会话添加到用户节点内的会话列表,也不知道如何在会话已存在时更新最新消息。
现有代码
fun addUser(userId: String) { val usersReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("Users") val userDetails = intent.getParcelableExtra<ConnectionDetailDTO>("USER_DETAILS") otherUserId = userDetails?.id!! currentUserId = currentUser?.id!! usersReference.child(userId).get().addOnSuccessListener { snapshot -> if (snapshot.exists()) { Log.d(TAG, "User already exists with ID: $userId") } else { val user = hashMapOf( "conversations" to listOf<Conversations>() ) usersReference.child(userId).setValue(true) .addOnSuccessListener { Log.d(TAG, "User created with ID: $userId") } .addOnFailureListener { e -> Log.d(TAG, "Error creating user: $e") } } }.addOnFailureListener { e -> Log.d(TAG, "Error checking user existence: $e") } }
使用的数据类
data class Conversations( val id: String = "", val latest_message: LatestMessage = LatestMessage(), val name: String = "", val otherUserId: Int = 0 ) { data class LatestMessage( val date: String = "", val isRead: Boolean = false, val message: String = "" ) } data class ChatUser( val id: Long = 0, // User ID val conversations: List<Conversations> = emptyList() )
解决方案
1. 修复用户创建逻辑
原代码中usersReference.child(userId).setValue(true)不符合目标结构,应该创建包含空会话列表的ChatUser对象存入数据库:
fun addUser(userId: String) { val usersReference = FirebaseDatabase.getInstance().getReference("Users") usersReference.child(userId).get().addOnSuccessListener { snapshot -> if (snapshot.exists()) { Log.d(TAG, "User already exists with ID: $userId") } else { // 创建符合结构的用户对象 val chatUser = ChatUser(id = userId.toLong(), conversations = emptyList()) usersReference.child(userId).setValue(chatUser) .addOnSuccessListener { Log.d(TAG, "User created with ID: $userId") } .addOnFailureListener { e -> Log.d(TAG, "Error creating user: $e") } } }.addOnFailureListener { e -> Log.d(TAG, "Error checking user existence: $e") } }
2. 实现会话的添加/更新逻辑
编写方法同时处理会话新增与更新,并且同步更新双方用户的会话记录:
fun updateOrAddConversation(currentUserId: String, otherUserId: String, otherUserName: String, currentUserName: String, messageContent: String) { val db = FirebaseDatabase.getInstance() val usersRef = db.getReference("Users") // 生成唯一会话ID:按用户ID排序,确保双方会话ID一致 val sortedIds = listOf(currentUserId, otherUserId).sorted() val timestamp = SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.getDefault()).format(Date()) val conversationId = "conversation_${sortedIds[0]}_${sortedIds[1]}_$timestamp" // 更新当前用户的会话 usersRef.child(currentUserId).child("conversations").get().addOnSuccessListener { snapshot -> val existingConversations = snapshot.getValue(object : GenericTypeIndicator<List<Conversations>>() {})?.toMutableList() ?: mutableListOf() val targetIndex = existingConversations.indexOfFirst { it.otherUserId == otherUserId.toInt() } val newLatestMsg = Conversations.LatestMessage( date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()), isRead = false, message = messageContent ) if (targetIndex != -1) { // 会话已存在,更新最新消息 existingConversations[targetIndex] = existingConversations[targetIndex].copy(latest_message = newLatestMsg) } else { // 会话不存在,添加新会话 val newConversation = Conversations( id = conversationId, latest_message = newLatestMsg, name = otherUserName, otherUserId = otherUserId.toInt() ) existingConversations.add(newConversation) } usersRef.child(currentUserId).child("conversations").setValue(existingConversations) } // 更新对方用户的会话 usersRef.child(otherUserId).child("conversations").get().addOnSuccessListener { snapshot -> val existingConversations = snapshot.getValue(object : GenericTypeIndicator<List<Conversations>>() {})?.toMutableList() ?: mutableListOf() val targetIndex = existingConversations.indexOfFirst { it.otherUserId == currentUserId.toInt() } val newLatestMsg = Conversations.LatestMessage( date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date()), isRead = false, message = messageContent ) if (targetIndex != -1) { existingConversations[targetIndex] = existingConversations[targetIndex].copy(latest_message = newLatestMsg) } else { val newConversation = Conversations( id = conversationId, latest_message = newLatestMsg, name = currentUserName, otherUserId = currentUserId.toInt() ) existingConversations.add(newConversation) } usersRef.child(otherUserId).child("conversations").setValue(existingConversations) } }
关键说明
- 会话ID生成:对用户ID排序后拼接,保证同一会话在双方用户节点下ID一致,避免重复创建。
- 双向同步:发送消息时必须同时更新双方的会话记录,确保数据一致性。
- 数据解析:使用
GenericTypeIndicator正确解析Firebase返回的List<Conversations>类型数据。
内容的提问来源于stack exchange,提问作者Sahar Batool
相关产品推荐
相关产品推荐

