OkHttpClient+WebSocketListener需配置心跳、指数退避重连及生命周期绑定,否则Android 12+必掉线;必须设置pingInterval(30, TimeUnit.SECONDS)、添加ACCESS_NETWORK_STATE权限,并在onMessage等回调中切回主线程更新UI。

直接用 OkHttpClient + WebSocketListener 就能跑通,但不加心跳、不处理重连、不绑定生命周期,上线后必掉线——尤其在 Android 12+ 后台限制收紧之后。
依赖和权限必须配齐
只加 okhttp 不够,logging-interceptor 虽非必需,但调试阶段没它等于蒙眼排错。网络权限也常被忽略,尤其是 ACCESS_NETWORK_STATE ——它不是摆设,断网重连逻辑依赖这个广播。
implementation("com.squareup.okhttp3:okhttp:4.12.0")implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
AndroidManifest.xml 中必须有:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
pingInterval 不是可选项,是保活刚需
OkHttp 的 pingInterval 控制的是客户端主动发 Ping 帧的频率,服务端收到后会自动回 Pong。这不是“建议心跳”,而是防止 NAT 超时、运营商中间件静默断连的硬性手段。默认不启用,必须显式设置。
- 值太小(如
5, TimeUnit.SECONDS)可能被服务器限频或拒绝 - 值太大(如超过 90 秒)在部分移动网络下大概率被断开
- 推荐起始值:
.pingInterval(30, TimeUnit.SECONDS),配合服务端Pong超时时间(通常为 ping 间隔 × 1.5)
连接必须在主线程外创建,但回调需切回主线程
client.newWebSocket() 是同步调用,但内部会启后台线程处理 IO;而所有 WebSocketListener 回调(onOpen、onMessage 等)默认在 OkHttp 自己的线程池中执行,**不是主线程**。UI 更新、LiveData 发送、Handler post 都得手动切换。
- 别在
onMessage里直接更新TextView,会 crash - Kotlin 推荐用
Handler(Looper.getMainLooper()).post { ... } - Java 可用
runOnUiThread或View.post
示例片段:
override fun onMessage(webSocket: WebSocket, text: String) {
Handler(Looper.getMainLooper()).post {
// 这里才能安全更新 UI
messageTextView.text = text
}
}
重连不能靠 try-catch,要靠状态机 + 指数退避
onFailure 和 onClosed 触发后,webSocket 实例已失效,不能再调 send() 或 close()。此时必须新建 Request 和新 WebSocket 实例——但不能立刻重试,否则容易触发服务器限流或本地线程耗尽。
- 首次失败后延迟
1_000ms重连 - 连续失败则按 2^n 指数增长(如 1s → 2s → 4s → 8s),上限建议 30s
- 记录重连次数,超过 5 次暂停自动重连,交由用户手动触发
- 重连前检查网络:用
ConnectivityManager判定是否真有网络,避免无意义轮询
最容易被跳过的点:重连时 URL 可能含临时 token,若 token 过期,重连永远失败——得先刷新凭证再建连接。


















