Kotlin Android中Supabase实时流collect()不执行且阻塞代码问题
聊天APP Supabase实时消息问题排查与修复
核心问题拆解
- 代码执行顺序颠倒:你先调用了
collect()(挂起阻塞函数),之后才执行channel.subscribe(),导致订阅代码根本没机会运行,自然收不到任何实时事件。 - 缺少消息过滤逻辑:当前监听的是整个
message表的插入操作,没有限定到当前chatRoomId,就算有新消息插入,也可能不是当前聊天室的内容,所以onEach代码块不会触发。 - RecyclerView更新方式错误:用
recyclerViewMessages.invalidate()无法正确刷新列表,必须调用适配器的notifyItemInserted()方法。 - JSON解析方式不合理:直接用
change.record.toString()会得到非标准JSON格式,应该用Supabase内置的序列化方法解析。
修复后的完整代码
private suspend fun realtimeCheck(scope: CoroutineScope) { try { channel = supabase.channel(chatRoomId) // 配置实时监听,添加当前聊天室的过滤条件(根据你的表字段调整) val dataFlow = channel.postgresChangeFlow<PostgresAction.Insert>(schema = "public") { table = "message" filter = "chat_room_id = '$chatRoomId'" } // 在协程域中启动Flow收集,避免阻塞当前流程 dataFlow.onEach { change -> println("收到实时消息: $change") // 直接解析record为目标数据类,无需手动转字符串 val newMessage = change.record.decode<data.message>() messageContainer.addMessage(newMessage) // 正确更新RecyclerView,必须在UI线程执行 runOnUiThread { val adapter = recyclerViewMessages.adapter adapter?.notifyItemInserted(messageContainer.messageList.size - 1) // 自动滚动到最新消息 recyclerViewMessages.scrollToPosition(messageContainer.messageList.size - 1) } }.catch { e -> e.printStackTrace() runOnUiThread { Toast.makeText(this@ChatScreen, "实时监听出错: ${e.message}", Toast.LENGTH_SHORT).show() } }.launchIn(scope) // 订阅频道,必须在Flow配置完成后执行 val subscribeResult = channel.subscribe(blockUntilSubscribed = true) println("频道订阅状态: ${channel.status.value}") if (!subscribeResult.isSuccess) { throw subscribeResult.error ?: Exception("频道订阅失败") } } catch (e: Exception) { e.printStackTrace() runOnUiThread { Toast.makeText(this@ChatScreen, e.message, Toast.LENGTH_SHORT).show() } } }
必做检查项
- Supabase后台配置:
- 进入Supabase控制台,找到
message表的Realtime标签,确保INSERT事件已启用。 - 确认行级安全策略允许当前用户读取对应聊天室的消息,否则会被拦截。
- 进入Supabase控制台,找到
- 协程域生命周期:传入的
scope要和页面生命周期绑定(比如用lifecycleScope或viewModelScope),防止内存泄漏。 - 数据类序列化:
message数据类必须添加@Serializable注解,否则无法正确解析:import kotlinx.serialization.Serializable @Serializable data class message( val id: String, val chat_room_id: String, val content: String, val sender_id: String // 其他字段根据你的表结构补充 )
内容的提问来源于stack exchange,提问作者user23732609
相关产品推荐
相关产品推荐

