🧩 一、前言
在现代 App 中,实时通信 已成为刚需:
- 聊天室消息秒达
- 直播间弹幕即时刷出
- 股票、币价数据实时刷新
这些背后都有一个核心技术支撑:👉 WebSocket
相比传统的 HTTP 轮询,WebSocket 是真正的「双向实时通信协议」。
⚙️ 二、WebSocket 原理简析
1️⃣ 通信模型
| 模式 | 特点 | 举例 |
|---|---|---|
| HTTP 请求响应 | 单向:客户端主动请求 | 适合一次性接口调用 |
| WebSocket | 双向:保持长连接,实时推送 | 聊天、推送、游戏状态同步 |
通信过程:
- 客户端发起 HTTP 握手请求(带 Upgrade 头)
- 服务端返回 101 Switching Protocols
- 从此连接升级为 WebSocket 通道
- 客户端与服务端可互发消息,无需重新建立连接
🔌 三、Android 上的 WebSocket 实现方案
常见实现库:
| 库 | 优点 | 备注 |
|---|---|---|
| OkHttp WebSocket ✅ | 稳定、高效、官方维护 | 推荐 |
| nv-websocket-client | 轻量、API 简洁 | 老项目兼容 |
| Java-WebSocket | 支持 SSL、心跳较灵活 | 适合游戏/独立服务 |
我们这里采用最推荐的 —— OkHttp WebSocket。
🧠 四、基本使用示例
1️⃣ 建立连接
val client = OkHttpClient()
val request = Request.Builder()
.url("wss://chat.example.com/ws")
.build()
val webSocket = client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
Log.d("WebSocket", "Connected")
}
override fun onMessage(webSocket: WebSocket, text: String) {
Log.d("WebSocket", "Received: $text")
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.d("WebSocket", "Closed: $reason")
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.e("WebSocket", "Error: ${t.message}")
}
})
💡 注意:
OkHttpClient会自动管理线程池,无需手动创建线程。
🧩 五、消息发送与心跳机制
1️⃣ 发送消息
webSocket.send("{"type": "chat", "content": "Hello!"}")
2️⃣ 发送心跳(保持连接)
建议每 30 秒发送一次:
val heartbeatJob = CoroutineScope(Dispatchers.IO).launch {
while (isActive) {
webSocket.send("{"type":"ping"}")
delay(30_000)
}
}
🧱 六、完整封装:WebSocketManager
下面是一个可直接用于生产环境的 Kotlin 封装类👇
package com.hatio.chat.websocket
import android.util.Log
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import okhttp3.*
object WebSocketManager {
private var webSocket: WebSocket? = null
private val client by lazy { OkHttpClient.Builder().build() }
private var reconnectAttempts = 0
private const val MAX_RECONNECT_ATTEMPTS = 5
private var isManualClose = false
private var heartbeatJob: Job? = null
private val _messageFlow = MutableSharedFlow<String>()
val messageFlow = _messageFlow.asSharedFlow()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
fun connect(url: String) {
if (webSocket != null) return
isManualClose = false
val request = Request.Builder().url(url).build()
webSocket = client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
Log.d("WebSocket", "Connected")
startHeartbeat()
reconnectAttempts = 0
}
override fun onMessage(webSocket: WebSocket, text: String) {
scope.launch { _messageFlow.emit(text) }
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.e("WebSocket", "Error: ${t.message}")
if (!isManualClose) reconnect(url)
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.d("WebSocket", "Closed: $reason")
stopHeartbeat()
}
})
}
fun send(message: String) {
webSocket?.send(message)
}
fun close() {
isManualClose = true
stopHeartbeat()
webSocket?.close(1000, "Manual Close")
webSocket = null
}
private fun reconnect(url: String) {
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) return
reconnectAttempts++
scope.launch {
delay(2000L * reconnectAttempts)
connect(url)
}
}
private fun startHeartbeat() {
heartbeatJob?.cancel()
heartbeatJob = scope.launch {
while (isActive) {
webSocket?.send("{"type":"ping"}")
delay(30_000)
}
}
}
private fun stopHeartbeat() {
heartbeatJob?.cancel()
}
}
🧠 七、在 ViewModel 中结合 Flow 实时接收消息
class ChatViewModel : ViewModel() {
val messages = WebSocketManager.messageFlow.asLiveData()
fun connect() {
WebSocketManager.connect("wss://chat.example.com/ws")
}
fun sendMessage(text: String) {
WebSocketManager.send("{"type":"chat", "msg":"$text"}")
}
override fun onCleared() {
super.onCleared()
WebSocketManager.close()
}
}
借助
SharedFlow实现消息实时流式分发,天然支持多个订阅者。
🧩 八、常见问题与优化策略
| 问题 | 原因 | 优化方案 |
|---|---|---|
| 🔄 断线重连 | 网络波动 | 指数退避算法延迟重连 |
| 💔 心跳失效 | 服务器断开连接 | 定时发送 ping/pong 检测 |
| ⚠️ 内存泄漏 | 未关闭协程或 socket | 使用生命周期感知组件 |
| ⏳ 消息延迟 | 主线程阻塞 | Flow + IO 协程异步接收 |
🔍 九、调试与测试技巧
- 使用 WebSocket 在线测试工具(例如 wss://echo.websocket.org)
- 抓包工具:Charles / Fiddler / Wireshark
- 模拟断网、延迟、重连场景
💡 十、面试常见问题总结
| 面试题 | 答案要点 |
|---|---|
| WebSocket 和 HTTP 的区别? | HTTP 是短连接,请求-响应模式;WebSocket 是持久双向连接。 |
| WebSocket 心跳机制怎么做? | 客户端定期发送 ping;服务器回复 pong。 |
| 如何实现断线重连? | 监听 onFailure/onClosed,使用延迟重连策略。 |
| 如何防止消息丢失? | 加入重发队列 + ACK 确认机制。 |
| OkHttp WebSocket 和 Java-WebSocket 有何区别? | 前者更轻量稳定,官方维护;后者跨平台但依赖更多。 |
🧭 十一、总结
| 模块 | 关键点 |
|---|---|
| 连接 | 使用 OkHttp 提供的稳定实现 |
| 心跳 | 定时发送 ping 包维持连接 |
| 重连 | 指数退避防止频繁重连 |
| 消息分发 | SharedFlow 实时推送 |
| 生命周期 | 与 ViewModel / Application 绑定 |
✨ 尾声
WebSocket 的核心价值在于「实时 + 双向 + 低延迟」。
在 IM、推送、游戏、协作等业务场景中,
合理的封装与心跳、重连策略,
能让你的应用像微信一样 稳定又实时。