移动端websocket切后台必断,需通过visibilitychange秒级感知、时间戳心跳替代readystate、指数退避重连、本地缓存+幂等校验实现不丢消息、不炸服务端。

移动端切到后台后 WebSocket 被系统杀死,不是代码没写好,而是 iOS 和 Android 主动冻结网络任务、暂停 JS 执行、回收连接的必然行为。你无法阻止它断开,但可以做到:秒级感知状态变化、干净重建连接、不丢消息、不炸服务端。
监听 visibilitychange 精准捕获切后台/唤醒时机
这是最轻量、兼容性最好、且必须做的第一层响应。不要等 onclose —— 它常常根本不触发。
- 页面切后台(锁屏、切 App、息屏)时,document.visibilityState === 'hidden' 会立即触发,此时记录时间戳,停掉主线程心跳定时器,但不要调 ws.close()(系统可能已断,再 close 会报 InvalidStateError)
- 页面切回前台时,visibilityState 变为 'visible',计算后台停留时长;若超过 10 秒,就执行重连流程;若很短(比如快速切回),可先尝试发个心跳探活,避免无谓重建
- 建议同时监听 pagehide(尤其 iOS 锁屏更可靠),并用 setTimeout 防抖 500ms,防止滚动遮挡等误判
用时间戳 + 心跳超时替代 readyState 判断连接有效性
ws.readyState === WebSocket.OPEN 在移动端完全不可信。Android WebView 常现“假连”:状态是 1,send() 却静默失败;iOS WKWebView 切后台后 readyState 可能卡住数分钟不更新。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 自己维护一个 lastHeartbeatAt 时间戳,每次收到服务端 pong 就更新
- 前端起一个定时器(如每 15 秒),检查距 lastHeartbeatAt 是否超 45 秒;超时即判定失效,主动 close 并触发重连
- 心跳包用业务层协议(如 {type: "ping"}),服务端必须回 {type: "pong"},不能只依赖 WebSocket 协议层 ping(JS 不暴露发送接口)
指数退避重连 + 资源清理防雪崩
重连不是“断了就马上 new WebSocket”,而是要克制、有序、可收敛。
- 每次重连前,先 clearInterval 清心跳、ws?.close() 关旧连接、置 ws = null,避免内存泄漏和事件重复绑定
- 重连间隔采用指数退避:[1000, 2000, 4000, 8000, 16000],第 5 次后固定为 30 秒,上限可控
- 建议加 随机抖动(+ Math.random() * 1000),避免大量客户端在服务端恢复瞬间集体涌进
- 设置 isReconnecting = true 标志,防止 onclose 或 visibilitychange 多次触发重连逻辑
离线消息本地缓存 + 服务端幂等校验
用户切后台期间发出的操作(如提交表单、点击确认、发送聊天),绝不能等到重连成功才发——业务窗口可能早已关闭。
- 所有关键 send() 前,先把消息写入 localStorage 或 IndexedDB,标记 status: "pending",附带唯一 messageId
- 重连成功后,按时间戳顺序取出 pending 消息重发,每条带上 messageId
- 服务端必须对 messageId 做幂等处理:收到重复 ID 直接返回成功,不二次执行业务逻辑
- PWA 场景下,可用 Service Worker 拦截并暂存消息,进一步提升离线鲁棒性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










