Android客户端仅接收Ktor SSE服务器一次推送,无法持续监听更新的问题求助
Android客户端仅接收Ktor SSE服务器一次推送,无法持续监听更新的问题求助
你好,我看了你的代码和描述,Postman测试正常但Android客户端只收一次推送就停止监听,大概率是客户端的协程管理或者连接配置出了问题,我帮你梳理几个可能的排查方向和解决办法:
1. 先修正ViewModel里的变量笔误(影响状态更新)
你ViewModel里的代码有个明显的笔误,_wsValue根本没定义,这会导致你无法正确观察后续的状态更新,先把它改过来:
private var _wsValue = MutableStateFlow<Resource<WhetherModule>?>(null) val wheatherState = _wsValue.asStateFlow()
2. 调整HttpClient超时配置,确保长连接不被断开
SSE是长连接,默认的HttpClient超时设置可能会让连接在闲置一段时间后被主动断开。你需要在创建HttpClient时调整超时参数,比如用OkHttp引擎的话:
val httpClient = HttpClient(OkHttp) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } install(Sse) // 必须安装SSE插件才能支持SSE功能 engine { config { // 根据你的需求设置超时,间隔长的话可以把读取超时设得更大,甚至禁用 connectTimeout(60, TimeUnit.SECONDS) readTimeout(60, TimeUnit.SECONDS) writeTimeout(60, TimeUnit.SECONDS) } } }
3. 改用channelFlow管理SSE连接生命周期
你提到试过channelFlow但没解决,可能是用法不对。普通flow处理长连接时,协程取消的逻辑不够灵活,channelFlow可以更好地控制连接的开启/关闭,还能方便添加重连逻辑:
fun getServerEvent() = channelFlow<Resource<WhetherModule>> { try { httpClient.sse( // 注意:模拟器用10.0.2.2对应电脑localhost,真机填服务器所在电脑的局域网IP host = "192.168.x.x", port = 8080, path = "/events" // 建议加斜杠,避免路径拼接出错 ) { Log.d("SSE", "已连接到服务器,开始监听事件") incoming.collect { event -> if (event.data != null) { val decodeData = Json.decodeFromString<WhetherModule>(event.data) send(Resource.Success(decodeData)) } } Log.d("SSE", "与服务器的连接已断开") } } catch (e: Exception) { Log.e("SSE", "连接异常: ${e.message}") send(Resource.Error(e)) close(e) } finally { close() } }.retryWhen { cause, attempt -> // 可选:添加自动重连逻辑,比如网络异常后重试3次 cause is IOException && attempt < 3 }
4. 排查ViewModel协程的运行状态
在ViewModel的receiveSSE函数里加日志,看看collect是否在第一次接收后就提前退出了:
private suspend fun receiveSSE (){ Log.d("ViewModelSSE", "开始监听SSE事件") postServer.getServerEvent().collect { e-> Log.d("ViewModelSSE", "收到事件: $e") _wsValue.value = e } Log.d("ViewModelSSE", "SSE监听已停止") }
如果日志显示"SSE监听已停止",说明incoming.collect()提前结束了,这时候要检查是服务器主动断开连接,还是客户端有异常被捕获,可以在客户端的catch块里打印详细异常信息排查原因。
5. 确认服务器端的SSE发送逻辑
虽然Postman测试正常,但可以在服务器端加日志,确保每次有新数据时都发送了SSE:
sse("/events") { try { dataFlow.collect { data -> log.info("准备发送SSE数据: $data") send(ServerSentEvent(data = Json.encodeToString(data), event = "data-event")) } } catch (e: Exception) { log.error("SSE收集异常: ${e.message}") } }
备注:内容来源于stack exchange,提问作者Ali Othman




