微信小程序围栏告警需用onlocationchange+startlocationupdatebackground组合,配requiredprivateinfos声明、gcj02坐标系及状态防抖,禁用setinterval轮询。

微信小程序端做地理位置围栏实时告警,核心不是“一直跑定位”,而是「精准触发 + 低功耗响应」。直接用 setInterval 轮询 uni.getLocation 在后台会失效、耗电高、审核大概率被拒;真正可行的路径是:用微信原生的 onLocationChange + startLocationUpdateBackground 组合监听,再配合射线法或高德/腾讯 SDK 做点在多边形内判断。
必须配置 requiredPrivateInfos,否则监听根本不会触发
这是最常卡住的第一步。微信基础库 ≥ 2.25.0 后,onLocationChange 和 startLocationUpdateBackground 不再自动可用,必须显式声明:
-
manifest.json的mp-weixin节点下,requiredPrivateInfos数组里至少包含"onLocationChange"和"startLocationUpdateBackground" - 对应
permission中,scope.userLocationBackground的desc必须单独写清“后台持续定位”用途,不能复用前台描述 - 漏掉任一字段,调用
uni.onLocationChange时控制台不报错,但回调永远不会执行——这是静默失败,极难排查
后台定位启动失败的三个硬性前提
uni.startLocationUpdateBackground() 不是调了就生效,它依赖三件事同时满足:
- 用户已手动授权
scope.userLocationBackground(仅uni.getSetting检查状态不够,必须uni.authorize主动申请) - 微信基础库版本 ≥ 2.25.0(开发者工具需关闭「调试基础库自动降级」,真机看右上角版本号)
- 小程序未被系统强杀(iOS 上首次调用可能返回
fail:system permission denied,建议加 1~2 秒延迟重试)
启动成功后,uni.onLocationChange 才开始吐数据,但首次回调延迟 5~15 秒属正常,别当成失败处理。
围栏判断必须用 GCJ02 坐标系,WGS84 直接传会误判
微信小程序 uni.getLocation 默认返回 WGS84 坐标,但腾讯地图、高德地图的围栏判断 API(如 AMap.GeometryUtil.isPointInPolygon 或 qqmap-wx-jssdk 的 areBound)只认 GCJ02(火星坐标)。不做转换会导致:明明在围栏内,判断结果却是 false。
- 方案一(推荐):调用
uni.getLocation({ type: 'gcj02' }),让微信直接返回 GCJ02 坐标(注意:仅微信小程序支持该参数) - 方案二:用开源库如
gcoord手动转换,但增加包体积且易出精度偏差 - 多边形顶点坐标也必须是同一坐标系——如果你围栏数据来自后端,确认它存的是 GCJ02 还是 WGS84,别混用
告警触发要防抖,避免重复弹窗或上报
位置变化事件每秒可能触发多次,而“进出围栏”是离散事件。直接在 onLocationChange 回调里每次调 isPointInPolygon 并弹窗,会导致:
- 同一秒内反复提示“已进入围栏”
- 服务端收到大量重复越界事件
- 用户感知为卡顿或 Bug
正确做法是加状态缓存 + 时间窗口防抖:
let lastInState = null;
let lastCheckTime = 0;
uni.onLocationChange(res => {
const now = Date.now();
if (now - lastCheckTime const isInside = checkPointInFence([res.longitude, res.latitude]);
if (isInside !== lastInState) {
lastInState = isInside;
uni.showToast({ title: isInside ? '已进入围栏' : '已离开围栏' });
// 此处发请求上报事件,带时间戳和状态
}
});
真实业务中,围栏顶点数量、坐标精度、设备 GPS 漂移都会影响判断稳定性。别指望一次判断就 100% 准确,建议结合连续 2~3 次判断一致再触发告警,比单次判断更可靠。










