App 锁屏就断连?我用 UTS 给 uni-app x 写了个「MQTT 四端后台保活」插件
做 IoT、远程监控、消息下发的小伙伴,一定都被「App 一进后台 / 一锁屏,MQTT 长连接就掉线」折磨过。这篇文章分享一个我发布的 uni-app x UTS 插件,用原生底层统一解决 Android / iOS / Web / 微信小程序 四端的 MQTT 后台保活与离线补发问题。
一、先说痛点
做 设备控制、远程监控、指令下发 类 App 时,MQTT 长连接几乎是标配。但实际落地会遇到一堆坎:
- App 一进后台、一锁屏,长连接很快被系统回收,设备直接变「离线」;
- 断线期间,设备上报的数据丢了、服务端下发的指令丢了;
- Android 各家厂商 ROM 疯狂杀后台,iOS 系统策略又不允许无限后台长连接;
- 想要四端都稳定,往往要写好几套平台判断逻辑,维护成本爆炸。
如果你们项目正好是 uni-app x,那可以直接用 UTS 原生插件把保活这件事彻底接管掉,业务层一行平台判断都不用写。
插件地址: ext.dcloud.net.cn/plugin?id=2…
二、插件能干什么
「MQTT 全端后台保活插件」(插件 ID:mqtt-keepalive)是一个 UTS 原生实现 的插件,用原生底层接管保活逻辑:
- Android / iOS / Web(H5) / 微信小程序 四端统一 API
- 业务层零平台判断代码,一套
start()走天下 - MQTT 3.1.1 客户端为原生自研(Kotlin / Swift),零三方原生依赖,不依赖 Paho 等 aar / framework,导入即用
- 支持 离线消息本地缓存补发,断线不丢数据
三、各端保活能力边界(重要,先看清)
| 平台 | 后台保活能力 | 说明 |
|---|---|---|
| Android | 强(前台服务 + 电源锁 + 网络监听) | 需用户允许常驻通知、加入电池白名单;部分厂商 ROM 需手动允许自启动 |
| iOS | 弱 | iOS 禁止无限后台长连接,仅前台可稳定维持;后台有限窗口,推荐配合 APNs 静默推送唤醒 |
| Web(H5) | 无 | 浏览器冻结 JS 线程,回到前台自动秒级重连 |
| 微信小程序 | 无 | 微信内核冻结 JS,回到前台自动重连并依赖服务端离线消息 |
⚠️ iOS 的「后台保活」需要服务端配合:设备离线时服务端通过 APNs 下发静默推送(
content-available)唤醒 App 重连同步。这块务必在接入文档里向最终客户如实说明,避免售后纠纷。
四、核心特性
- Android 原生保活:前台服务 + 常驻通知 + WakeLock 电源锁 + WifiLock + 网络监听 + 指数退避自动重连 + 原生 SQLite 离线消息缓存补发;
- iOS 有限后台保活:后台任务申请 + 生命周期补偿 + MQTT(TCP/TLS) 客户端 + 内存离线缓存补发;
- Web / 小程序休眠补偿:页面隐藏自动停心跳、回到前台秒级重建连接;
- 离线消息缓存:设备上报、控制指令断线期间本地缓存,重连后按序批量补发,不丢数据(
onCacheFlush回调告知补发数量); - 零三方原生依赖:自研 Kotlin / Swift MQTT 客户端,导入即用;
- 权限引导:一键跳转电池优化白名单、厂商自启动管理、应用详情设置,Android 13+ 通知权限引导;
- 开机自启 + 进程被杀恢复:
BootReceiver+ 配置持久化 +START_STICKY重建连接。
五、快速开始
导入插件后,在需要保活的页面引入:
import { start, stop, getStatus } from '@/uni_modules/mqtt-keepalive'
启动保活:
start({
clientId: 'device_001',
broker: 'mqtt://your-broker.com', // Android/iOS 支持 mqtt/mqtts/tcp/ssl;Web/小程序必须 ws/wss
port: 1883,
username: 'user',
password: 'pass',
heartInterval: 30, // 后台心跳(秒)
enableWakeLock: true, // Android 电源锁
foregroundTitle: '设备监控运行中',
foregroundContent: 'MQTT 长连接保活中',
cacheLimit: 500, // 离线缓存上限(条)
}, {
onStatusChange: (e) => {
// e.status: connecting / online / reconnecting / offline / stopped
console.log('状态变化', e.status, e.reason)
},
onMessage: (e) => {
console.log('收到消息', e.topic, e.payload)
},
onCacheFlush: (e) => {
console.log('离线缓存补发完成', e.count)
},
onWarning: (e) => {
console.log('告警', e.code, e.message)
},
})
发布消息(离线自动入缓存,重连后按序补发):
publish({
topic: 'device_001/cmd',
payload: JSON.stringify({ action: 'open' }),
qos: 1,
})
订阅主题:
subscribe(['device_001/cmd', 'device_001/status'], 1)
六、API 一览
| API | 说明 |
|---|---|
start(options, callbacks) | 启动 MQTT 保活,核心配置见下表 |
stop() | 停止保活并断开连接 |
publish(options): boolean | 发布消息,离线自动缓存、重连后按序补发 |
subscribe(topics[], qos?): boolean | 订阅主题 |
getCacheCount(): number | 获取当前未补发的离线缓存条数 |
resume() | 回到前台 / 收到推送时手动触发重连(iOS 配合 APNs 静默推送唤醒) |
getStatus(): MqttKeepAliveSnapshot | 返回 { status, reconnectCount, cacheCount } |
openBatteryOptimizationSettings() | 跳转电池优化白名单(Android) |
openAutoStartSettings() | 跳转厂商自启动管理(小米/华为/OPPO/vivo) |
openAppDetailSettings() | 跳转应用详情设置 |
requestNotificationPermission() | 请求通知权限(Android 13+) |
start() 常用配置项:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| clientId | string | - | 客户端 ID |
| broker | string | - | 服务地址 |
| port | number | 按协议 | 端口 |
| username / password | string | - | 认证 |
| heartInterval | number | 30 | 后台心跳间隔(秒) |
| foregroundHeartInterval | number | 60 | 前台心跳间隔(秒) |
| enableWakeLock | boolean | true | Android 电源锁 |
| foregroundTitle / foregroundContent | string | - | 常驻通知文案 |
| cacheLimit | number | 500 | 离线缓存上限 |
| reconnectMin / reconnectMax | number | 1 / 30 | 断线重连指数退避区间(秒) |
| qos | number | 1 | 默认 QoS |
| subscribeOffline | boolean | true | 是否订阅离线补发主题 ${clientId}/offline |
七、常见问题
Q:锁屏后还是断线?
A:请先调用 openBatteryOptimizationSettings() 加入电池白名单,并允许常驻通知;部分厂商(小米/华为/OPPO/vivo)还需 openAutoStartSettings() 允许自启动。
Q:断线期间的数据会丢吗? A:不会。Android 端上报与控制指令会写入原生 SQLite,重连后自动按序补发;Web/小程序端依赖服务端离线缓存。
Q:iOS 能像 Android 一样后台常驻吗? A:不能,这是 iOS 系统策略。请使用 APNs 静默推送唤醒方案,服务端需配合改造。
八、适用场景
- IoT 设备远程控制与状态上报
- 室内/园区/养殖等环境监控
- 门禁、充电桩、共享设备等工业控制类 App
- 需要后台实时推送替代方案的业务
总结
如果你的项目是 uni-app x,正好需要 MQTT 长连接 + 后台保活 + 离线不丢消息,这个插件可以帮你省掉大量的原生端适配工作,四端统一 API、零平台判断、零三方原生依赖,导入即可用。
插件地址: ext.dcloud.net.cn/plugin?id=2…
有疑问欢迎在插件评论区留言,或在文末交流,感谢支持 🙏