目录
- WebSocket 是什么? (为什么需要它)
- Android 中使用 WebSocket 的几种方式
- 实战:使用 OkHttp 实现 WebSocket (推荐)
- 实战:使用 Java-WebSocket 库
- 生命周期管理与最佳实践
- 高级主题:心跳机制、重连策略
- 总结与对比
WebSocket 是什么?
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。

- 传统 HTTP: 客户端发起请求,服务器响应,服务器不能主动向客户端推送数据(除非使用轮询或长轮询,效率低)。
- WebSocket: 客户端和服务器之间建立一个持久连接,双方都可以随时向对方发送数据,就像一个电话,打通后,双方可以随时说话。
为什么在 Android 中使用 WebSocket?
- 实时性: 对于需要实时更新的应用,如聊天室、股票行情、在线游戏、体育赛事直播等,WebSocket 是最佳选择。
- 高效性: 避免了 HTTP 轮询带来的大量冗余请求和服务器压力,节省了带宽和电量。
- 低延迟: 数据可以瞬间从服务器推送到客户端。
Android 中使用 WebSocket 的几种方式
主要有两种主流的实现方式:
使用成熟的网络库 (推荐)
这是最常见、最稳定的方式,它们封装了底层的细节,提供了简洁的 API,并且通常与 OkHttp、Retrofit 等库生态良好集成。
- OkHttp: Square 公司出品,是 Android 事实上的标准网络库,从 OkHttp 3.5.0 开始,官方就内置了对 WebSocket 的支持,这是最推荐的方式,因为它与 OkHttp 的连接池、拦截器等无缝集成。
- Java-WebSocket: 一个专门为 Java 设计的 WebSocket 客户端/服务器库,功能强大,API 也比较直观,也是一个不错的选择。
使用 Android 原生 API (不推荐)
Android 8.0 (API 26) 引入了 java.net.http 包,其中包含了 WebSocket 的客户端 API (WebSocket)。

- 优点: 不需要引入第三方库。
- 缺点:
- 兼容性差: 仅支持 Android 8.0+,对于需要兼容旧版本的项目来说不可用。
- API 复杂: 回调机制相对繁琐。
- 生态不完善: 社区支持、文档和示例代码远不如第三方库。
除非你有非常特殊的需求,否则强烈建议使用 OkHttp 或 Java-WebSocket。
实战:使用 OkHttp 实现 WebSocket (推荐)
这是最主流、最优雅的方式。
步骤 1: 添加依赖
在你的 app/build.gradle 文件中添加 OkHttp 依赖:
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0") // 请使用最新版本
}
步骤 2: 创建 WebSocket 客户端
OkHttp 的 WebSocket 是一个简单的类,你需要手动管理它的生命周期(连接、发送、关闭)。
import okhttp3.*
import okio.ByteString
import java.util.concurrent.TimeUnit
class OkHttpWebSocketManager {
private val client: OkHttpClient = OkHttpClient.Builder()
.pingInterval(30, TimeUnit.SECONDS) // 设置心跳间隔
.build()
private var webSocket: WebSocket? = null
// 启动 WebSocket 连接
fun connect(url: String) {
val request = Request.Builder()
.url(url)
.build()
webSocket = client.newWebSocket(request, MyWebSocketListener())
}
// 发送消息
fun sendMessage(message: String) {
webSocket?.send(message)
}
// 关闭连接
fun close() {
webSocket?.close(1000, "Normal closure") // 1000 表示正常关闭
// 重要:关闭后,OkHttp 的 Dispatcher 仍然持有这个 WebSocket,
// 需要调用 cancel 来释放资源
client.dispatcher.executorService.shutdown()
}
// OkWebSocket 的监听器
private inner class MyWebSocketListener : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
// 连接成功
println("WebSocket 连接成功!")
// 连接成功后可以发送第一条消息
webSocket.send("Hello, Server!")
}
override fun onMessage(webSocket: WebSocket, text: String) {
// 收到文本消息
println("收到消息: $text")
// 在这里更新 UI,例如使用 runOnUiThread
// activity.runOnUiThread { ... }
}
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
// 收到二进制消息
println("收到二进制消息: ${bytes.hex()}")
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
// 连接正在关闭
println("连接正在关闭: code=$code, reason=$reason")
webSocket.close(1000, null) // 可以在这里确认关闭
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
// 连接已完全关闭
println("连接已关闭: code=$code, reason=$reason")
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
// 连接失败
println("WebSocket 连接失败: ${t.message}")
// 在这里可以触发重连逻辑
}
}
}
步骤 3: 在 Activity/ViewModel 中使用
注意: 网络操作和回调都在子线程中,如果需要更新 UI,必须切回主线程。
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import okhttp3.Response
import okhttp3.WebSocket
import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
private lateinit var webViewManager: OkHttpWebSocketManager
private lateinit var statusText: TextView
private lateinit var sendButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
statusText = findViewById(R.id.statusText)
sendButton = findViewById(R.id.sendButton)
webViewManager = OkHttpWebSocketManager()
sendButton.setOnClickListener {
webViewManager.sendMessage("这是来自客户端的消息!")
}
// 启动连接
webViewManager.connect("ws://echo.websocket.org") // 这是一个测试服务器,会回显你发送的消息
}
override fun onDestroy() {
super.onDestroy()
// 在 Activity 销毁时,务必关闭 WebSocket 连接,防止内存泄漏
webViewManager.close()
}
}
实战:使用 Java-WebSocket 库
如果你不使用 OkHttp,或者更喜欢这个库的 API,它也是一个很好的选择。
步骤 1: 添加依赖
dependencies {
implementation("org.java-websocket:Java-WebSocket:1.5.5") // 请使用最新版本
}
步骤 2: 创建客户端
import org.java_websocket.client.WebSocketClient
import org.java_websocket.handshake.ServerHandshake
import java.net.URI
class JavaWebSocketClient(uri: URI) : WebSocketClient(uri) {
override fun onOpen(handshake: ServerHandshake?) {
println("Java-WebSocket 连接成功!")
// 连接成功后发送消息
send("Hello from Java-WebSocket Client!")
}
override fun onMessage(message: String?) {
println("收到消息: $message")
}
override fun onClose(code: Int, reason: String?, remote: Boolean) {
println("连接关闭: code=$code, reason=$reason, remote=$remote")
}
override fun onError(ex: Exception?) {
println("发生错误: ${ex?.message}")
}
}
步骤 3: 使用客户端
// 在 Activity 或其他地方
val client = JavaWebSocketClient(URI("ws://echo.websocket.org"))
client.connect() // 注意:connect 是异步的
生命周期管理与最佳实践
这是 Android WebSocket 开发中最关键的部分。
问题:内存泄漏
WebSocket 连接是一个长期存在的任务,Activity/Fragment 销毁了,但 WebSocket 连接没有关闭,它仍然会持有 Activity 的引用(例如在回调中),导致 Activity 无法被回收,造成内存泄漏。
解决方案:
-
在
onDestroy中关闭连接: 这是最基本的一步,当你的 UI 组件销毁时,必须调用close()方法。override fun onDestroy() { super.onDestroy() webViewManager.close() } -
使用 ViewModel (强烈推荐): ViewModel 专门用于保存和管理与 UI 相关的数据,当屏幕旋转导致 Activity 重建时,ViewModel 不会销毁,因此可以安全地持有 WebSocket 连接,避免因屏幕旋转而断开重连。
class MyViewModel : ViewModel() { private val _messages = MutableLiveData<String>() val messages: LiveData<String> = _messages private val webSocketManager = OkHttpWebSocketManager() init { // 设置监听器,将消息更新到 LiveData webSocketManager.setListener { message -> _messages.postValue(message) } webSocketManager.connect("ws://your-server-url") } fun sendMessage(message: String) { webSocketManager.sendMessage(message) } // ViewModel 被清除时(当它对应的 Activity 真正销毁时) override fun onCleared() { super.onCleared() // 关闭 WebSocket 连接 webSocketManager.close() } } -
在 UI 层处理 LiveData: 在你的
Activity或Fragment中,观察 ViewModel 的LiveData来更新 UI。class MyActivity : AppCompatActivity() { private lateinit var viewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this).get(MyViewModel::class.java) viewModel.messages.observe(this) { message -> statusText.text = message } } }
高级主题:心跳机制、重连策略
生产环境中的 WebSocket 需要更健壮的设计。
心跳机制
网络中间设备(如路由器、NAT)可能会因为长时间没有数据传输而断开连接,为了保持连接活跃,客户端需要定期向服务器发送一个简单的“心跳”消息({"type": "ping"}),服务器收到后回复一个“心跳应答”({"type": "pong"})。
- OkHttp 的
pingInterval: OkHttp 已经内置了心跳机制,通过client.newBuilder().pingInterval(...)设置即可,它会自动在后台发送 Ping 帧(这是一种底层的 WebSocket 帧,不涉及业务逻辑),服务器会自动响应 Pong 帧。 - 自定义心跳: 如果你的业务协议有自己的心跳格式,你可以在
onOpen成功后,启动一个定时任务,定期发送自定义的心跳消息。
// 在 MyWebSocketListener 的 onOpen 中
private val heartBeatJob = CoroutineScope(Dispatchers.IO).launch {
while (isActive) {
delay(30000) // 30秒
webSocket.send("{\"type\":\"heartbeat\"}")
}
}
// 在 onClosed 或 onFailure 中取消
heartBeatJob.cancel()
重连策略
网络可能不稳定,连接随时可能断开,一个健壮的客户端应该具备自动重连的能力。
- 检测断开: 在
WebSocketListener的onFailure或onClosed回调中,可以认为连接已经断开。 - 指数退避算法: 不要立即重连,这会给服务器带来压力,应该采用“指数退避”策略,即每次重连的等待时间逐渐增加(1s, 2s, 4s, 8s...),直到达到一个最大值。
- 实现逻辑:
- 在
onFailure中,启动一个重连计数器。 - 根据计数器计算等待时间。
- 使用
Handler或Coroutine在等待时间后再次调用connect()。 - 如果重连成功,重置计数器。
- 如果用户手动关闭(例如退出登录),则取消重连逻辑。
- 在
// 在 OkHttpWebSocketManager 中添加重连逻辑
private var reconnectAttempts = 0
private val maxReconnectAttempts = 5
private val handler = Handler(Looper.getMainLooper())
private fun scheduleReconnect() {
if (reconnectAttempts >= maxReconnectAttempts) {
println("重连次数已达上限,停止重连")
return
}
val delay = (1000L * (1 shl reconnectAttempts.coerceAtMost(4))) // 指数退避,最大16秒
println("准备第 ${reconnectAttempts + 1} 次重连,等待 ${delay}ms")
handler.postDelayed({
reconnectAttempts++
connect(url) // 重新连接
}, delay)
}
// 修改 onFailure
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
println("WebSocket 连接失败: ${t.message}")
scheduleReconnect() // 调度重连
}
// 在手动关闭或连接成功时重置状态
override fun onOpen(webSocket: WebSocket, response: Response) {
println("WebSocket 连接成功!")
reconnectAttempts = 0 // 重置重连次数
// ...
}
override fun close() {
handler.removeCallbacksAndMessages(null) // 取消所有待执行的重连任务
// ... 原有关闭逻辑
}
总结与对比
| 特性 | OkHttp | Java-WebSocket | Android 原生 API |
|---|---|---|---|
| 易用性 | 高 (与 OkHttp 生态无缝集成) | 中等 (API 直观但独立) | 低 (API 繁琐) |
| 稳定性 | 高 (成熟稳定,广泛使用) | 高 (老牌库) | 一般 (较新,社区支持少) |
| 兼容性 | 高 (支持所有 Android 版本) | 高 (纯 Java,兼容性好) | 低 (仅 Android 8.0+) |
| 生态集成 | 极高 (与 Retrofit, Moshi 等完美配合) | 低 (独立生态) | 无 |
| 功能 | 强大 (内置心跳、连接池) | 强大 (功能全面) | 基础 |
| 推荐度 | ⭐⭐⭐⭐⭐ (首选) | ⭐⭐⭐⭐ (备选) | ⭐ (不推荐) |
最终建议:
对于绝大多数 Android 项目,直接使用 OkHttp 是最简单、最可靠、最符合现代 Android 开发实践的选择,请务必结合 ViewModel 进行生命周期管理,并根据需要实现 心跳和重连 机制,以确保应用的健壮性。
