app端心跳包需结合平台特性保活:ios须开启background modes(如audio)否则30秒断连;android需引导用户关闭省电优化,低电量时降频发送;真后台应弃用js定时器,改用原生事件或云函数通过“最后活跃时间”兜底判断在线状态。

App端怎么发心跳包不被系统杀掉
iOS 和 Android 后台运行时,定时器(setInterval)和网络请求大概率被系统休眠或终止,直接用前端逻辑做心跳几乎必然失效。云开发侧不能只等前端“主动上报”,得靠客户端在前台/后台切换时主动触发、并配合平台特性保活。
- iOS:必须开启
Background Modes中的audio或location(仅作保活用途,不实际使用),否则 App 进入后台 30 秒内心跳就断;uni-app 需在manifest.json的ios节点里配"usesBackgroundModes": ["audio"] - Android:部分厂商(华为、小米、OPPO)会强杀后台进程,需引导用户手动关闭“省电优化”;uni-app 可调用
uni.getBatteryInfo判断是否低电量,低电量时降频发送(比如从 30s 一次改为 2min 一次) - 真后台心跳不能依赖 JS 定时器——改用
plus.runtime.getProperty+plus.push.addEventListener('click', ...)等原生事件兜底,或借助uni.getPushClientId获取设备唯一 ID 后,在云函数里做“最后活跃时间”兜底判断
云函数里怎么判断用户“还在线”
不能只看最后一次心跳时间戳是否
- 每次心跳更新用户文档的
lastHeartbeatTime字段(Date类型),同时设一个isOnline布尔字段为true - 另起一个云函数(如
checkOnlineStatus),每 2 分钟触发一次(用云开发的定时触发器),查所有lastHeartbeatTime超过 90 秒的记录,把isOnline改为false;注意加索引:lastHeartbeatTime升序 +isOnline: true - 别用
new Date().getTime()存毫秒数——云函数和客户端时钟可能不同步,统一用new Date()存 ISO 字符串(如"2024-05-20T10:23:45.123Z"),避免时区换算错误
前端怎么避免重复发心跳或漏发
uni-app 的 onHide / onShow 生命周期不可靠(尤其 Android 多任务切换时),单纯靠它们启停定时器容易丢包或堆积请求。
- 心跳定时器必须全局单例:声明一个
heartBeatTimer变量,每次启动前先clearInterval(heartBeatTimer),再重新赋值 - 发心跳前先检查网络:
uni.getNetworkType返回none或unknown就跳过,避免失败重试雪崩 - 用
uni.setStorageSync('lastHeartbeat', Date.now())记本地时间戳,App 再次启动时比对:如果距上次心跳已超 120 秒,立刻补发一次,再启动定时器 - 云函数返回 401 或用户 token 过期时,前端必须立即停止心跳并清空定时器,否则持续报错占资源
怎么让聊天列表实时显示“在线/离线”状态
不能每次滚动都查一遍用户在线状态——数据库读 QPS 会爆。得用“状态缓存 + 变更通知”组合策略。
- 云数据库集合里建一个
userStatus表,只存userId、isOnline、updatedAt三个字段,精简结构,方便高频读 - 前端用
uniCloud.database().collection('userStatus').watch(...)监听变化(仅限 App 端,H5 不支持),收到变更后局部更新对应头像旁的小绿点 - watch 的
filter必须写明确条件,比如{ userId: { $in: ['u1','u2','u3'] } },否则监听全表会触发频繁同步、耗电剧增 - watch 断连后自动重连有延迟,首次进入页面时仍要主动查一次
userStatus,避免白屏等待
最麻烦的不是写心跳逻辑,而是处理“用户以为自己在线,其实心跳早断了”这种状态错位——设备时间不准、云函数执行失败、watch 连接闪断,都会导致界面显示和真实状态不一致。建议在关键操作(比如发消息前)再同步校验一次对方状态,而不是全信缓存。











