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

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

火山引擎 最新活动