移动端浏览器无后台保活能力,流式连接被系统挂起实为资源回收或网络栈冻结;需结合可见性、网络连通性与业务心跳三重信号识别连接状态。

移动端浏览器(尤其是 iOS Safari 和 Android WebView)没有后台保活能力,所谓“流式连接被系统强行挂起”,本质是系统在页面不可见后主动回收资源或冻结网络栈——此时 WebSocket 或 Server-Sent Events(SSE)连接大概率已断开,但前端可能还停留在 OPEN 状态。识别这一状态不能依赖连接本身,而要结合可见性、网络连通性与业务心跳三重信号。
监听页面可见性变化
这是最直接、最及时的前台/后台感知手段:
- 注册
document.addEventListener('visibilitychange', ...),通过document.visibilityState判断当前状态:visible表示在前台,hidden表示已切后台(含锁屏、App 切换、浏览器标签切换) - 进入
hidden状态时,立即暂停所有心跳发送(避免无效轮询耗电),但不调用ws.close()——此时连接可能已被系统静默断开,强制 close 会抛异常 - 恢复
visible后,不要复用旧连接,而是先检查ws.readyState;若不等于WebSocket.OPEN,立刻触发重连流程
叠加业务层心跳验证
仅靠 readyState 不可靠,尤其在 Android WebView 中存在状态延迟更新问题:
- 客户端每 30 秒向服务端发一次业务心跳(非 TCP keepalive),服务端收到后回写时间戳或确认标识
- 客户端维护本地最近一次成功响应的时间戳,若距当前超过 45 秒,即判定连接失效,无论
readyState值为何 - 心跳失败时,启动指数退避重连:首次等待 1s,失败则 2s、4s、8s…上限设为 30s,防止网络未恢复时密集刷连
结合系统网络状态做兜底判断
网络断开是连接失效的强信号,但需注意 Android/iOS 对 navigator.onLine 的实现差异较大,不能单独依赖:
- 监听
window.addEventListener('online')和'offline',作为辅助线索,但不作为唯一依据 - 更稳妥方式是使用
fetch('/health')发起轻量探测请求(如 GET /api/ping),超时(建议设为 5–8s)即视为网络异常 - 当
visibilityState === 'hidden'且连续两次探测失败,可提前标记连接为“疑似中断”,待切回前台时优先重建
服务端协同校验连接活性
前端无法控制后台存活,但可和服务端配合降低误判和消息丢失风险:
- 每次成功建立 WebSocket 连接后,向服务端上报唯一
clientId及连接建立时间戳 - 服务端对每个 client 维护
lastHeartbeatTime,若超时(如 60s)未收到心跳,主动清理 session 并缓存离线消息摘要 - 客户端切回前台后,先发起一次
/api/offline-summary请求拉取未读摘要,再建立新连接,避免消息空窗期










