如果你以为一个游戏启动后只靠本地逻辑运转,那你真是低估了网络通信的存在感。在互动组件项目里,WebSocket 才是真正的灵魂通道——UI 再好看,没包发出去也等于白搭。
本章我们深入讲解 WebSocket 在安卓前端项目中的应用,从封装结构到消息分发,从心跳维持到断线重连,用实际的项目代码完整梳理一遍消息通信流程。
首先是通信初始化部分。项目的 Socket 一般集中封装在 NetManager 中。以下是一个典型实现:
const NetManager = {
ws: null,
isConnected: false,
reconnectTimer: null,
heartbeatTimer: null,
connect(url) {
if (this.ws && this.isConnected) return;
this.ws = new WebSocket(url);
this.ws.binaryType = "arraybuffer";
this.ws.onopen = this._onOpen.bind(this);
this.ws.onmessage = this._onMessage.bind(this);
this.ws.onclose = this._onClose.bind(this);
this.ws.onerror = this._onError.bind(this);
},
_onOpen() {
console.log("连接服务器成功");
this.isConnected = true;
this._startHeartbeat();
},
_onClose() {
console.warn("服务器连接断开");
this.isConnected = false;
this._reconnect();
},
_onError(err) {
console.error("WebSocket 连接错误", err);
},
_onMessage(event) {
let data = new Uint8Array(event.data);
ProtocolManager.decodeMessage(data);
},
_reconnect() {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = setTimeout(() => {
this.connect("ws://your-server-address");
}, 3000);
},
_startHeartbeat() {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = setInterval(() => {
this.send(ProtocolManager.encodeHeartbeat());
}, 5000);
},
send(data) {
if (!this.isConnected) return;
this.ws.send(data);
}
};
这段代码具备连接管理、断线重连、心跳保活三大能力。只要初始化时调用 connect 即可自动绑定监听事件。
接下来是协议设计。客户端与服务器之间的通信是基于自定义协议的,通常结构如下:
[消息ID][消息体长度][消息体]
为了更规范,建议封装 ProtocolManager:
const ProtocolManager = {
encodeMessage(msgId, payload) {
let body = JSON.stringify(payload);
let bodyBuffer = new TextEncoder().encode(body);
let buffer = new Uint8Array(4 + bodyBuffer.length);
buffer[0] = (msgId >> 8) & 0xFF;
buffer[1] = msgId & 0xFF;
buffer[2] = (bodyBuffer.length >> 8) & 0xFF;
buffer[3] = bodyBuffer.length & 0xFF;
buffer.set(bodyBuffer, 4);
return buffer;
},
decodeMessage(buffer) {
let msgId = (buffer[0] << 8) | buffer[1];
let bodyLen = (buffer[2] << 8) | buffer[3];
let json = new TextDecoder().decode(buffer.slice(4, 4 + bodyLen));
let payload = JSON.parse(json);
MessageDispatcher.dispatch(msgId, payload);
},
encodeHeartbeat() {
return this.encodeMessage(0x0001, {});
}
};
服务器返回后,前端需要有一个调度系统负责分发:
const MessageDispatcher = {
listeners: {},
register(msgId, callback, context) {
this.listeners[msgId] = { callback, context };
},
dispatch(msgId, data) {
if (!this.listeners[msgId]) {
console.warn("未处理的消息:", msgId);
return;
}
let { callback, context } = this.listeners[msgId];
callback.call(context, data);
}
};
示例注册方式如下:
MessageDispatcher.register(0x1001, this.onHallData, this);
断线重连与心跳机制是必不可少的,尤其在安卓端容易被系统后台挂起或回收,断线重连可以提升稳定性。心跳一般是定时每 5 秒发一个“空包”:
setInterval(() => {
NetManager.send(ProtocolManager.encodeHeartbeat());
}, 5000);
如果连接断开,NetManager 会触发 reconnect,通过定时器进行重新连接尝试。
下面是一个完整的发包→收包→界面处理流程:
let payload = {
userId: UserManager.userId
};
NetManager.send(ProtocolManager.encodeMessage(0x1003, payload));
服务器返回包结构如下:
{
"nickname": "player001",
"avatar": "icon_12.png",
"level": 5,
"gender": 1
}
接收方式如下:
MessageDispatcher.register(0x1004, function (data) {
UIManager.showUI("UIUserInfo", (ui) => {
ui.updateUserInfo(data);
});
}, this);
到这里为止,客户端发包、收包、处理 UI 的整个流程已经完整建立。
总结一下:这一章我们完成了客户端通信模块的封装与调用实践,包括了 WebSocket 的初始化、协议设计、消息派发与重连策略。下一章将进入 Node.js 服务端,看看服务器是如何处理这些消息与房间广播的。