App 锁屏就断连?我用 UTS 给 uni-app x 写了个「MQTT 四端后台保活」插件

4 阅读5分钟

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 需手动允许自启动
iOSiOS 禁止无限后台长连接,仅前台可稳定维持;后台有限窗口,推荐配合 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() 常用配置项:

字段类型默认值说明
clientIdstring-客户端 ID
brokerstring-服务地址
portnumber按协议端口
username / passwordstring-认证
heartIntervalnumber30后台心跳间隔(秒)
foregroundHeartIntervalnumber60前台心跳间隔(秒)
enableWakeLockbooleantrueAndroid 电源锁
foregroundTitle / foregroundContentstring-常驻通知文案
cacheLimitnumber500离线缓存上限
reconnectMin / reconnectMaxnumber1 / 30断线重连指数退避区间(秒)
qosnumber1默认 QoS
subscribeOfflinebooleantrue是否订阅离线补发主题 ${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…

有疑问欢迎在插件评论区留言,或在文末交流,感谢支持 🙏