一、为什么后台定位这么难?

做过外勤、物流、配送、巡检类 App 的开发者,几乎都踩过这些坑:

Android

  • 8.0 以后后台限制越来越严,普通定位服务很容易被系统杀掉
  • 必须前台服务(FGS)+ 常驻通知,Android 13+ 还要单独申请通知权限
  • 各厂商(小米、华为、OPPO、vivo)电池优化、自启动白名单各不相同
  • Android 14 还要声明 foregroundServiceType=location

iOS

  • 后台定位必须开 Background Modes location
  • 「始终允许」权限要分步申请,App Store 审核对用途说明要求很严
  • App 挂起后定位回调可能中断,需要额外处理恢复逻辑

鸿蒙 Next

  • 权限要写在 module.json5,只写 config.json 不会进包
  • 后台定位需要长时任务(LOCATION 类型),还要定时续期
  • 平板 / 2in1 要在主工程和插件两侧分别配置 deviceTypes

自己从零写三端原生代码,周期以周计,还要持续跟进系统策略变化。有没有一套 uni-app 能直接用的方案?

二、keepalive-location:三端统一的 UTS 后台定位插件

keepalive-location 是一个 UTS 原生插件,用一套 JavaScript API 覆盖 Android、iOS、鸿蒙 Next 的后台持续 GPS 定位场景,典型用途包括:

  • 外勤巡检轨迹记录
  • 物流配送实时位置
  • 司机出车路线留痕
  • 现场作业任务核验

核心能力一览

能力 说明
后台定位启停 startLocation() / stopLocation()
点位回调 经纬度、精度、速度、高度、时间戳、provider
权限内置 检测、申请、跳系统设置,无需另装权限插件
状态监听 权限丢失、长时任务过期、服务异常、上报失败等
本地环缓 可配置最大缓存点数,断网不丢点
可选 HTTPS 上报 批量 POST 到业务服务器,不默认上传第三方
坐标转换 WGS84 ↔ 高德 GCJ-02 ↔ 百度 BD-09
统一错误码 803xxxx 分段,便于日志与客服排查

三、三端分别做了什么?

Android:前台服务 + 唤醒锁 + 厂商白名单

  • 内置 TrackForegroundService,适配 Android 8.0–14
  • 支持常驻通知、WakeLock
  • openVendorKeepAliveSettings() 深链跳转小米 / 华为荣耀 / OPPO / vivo / 三星常见保活设置页
  • 正式包务必 initTrack({ enableForegroundService: true })

iOS:Background Modes + Always 分步授权

  • 支持 Background Modes location
  • Always 权限分步申请,挂起状态持续出点
  • readme 附带 App Store 审核文案模板,可直接改写提交

鸿蒙 Next:长时任务自动续期

  • 权限声明在 module.json5(含平板 tablet、2in1 支持)
  • 长时任务类型 LOCATION,定时续期,监听取消事件(8031004
  • ContinuousLocationRequest 持续定位,intervalMs 最高 60 秒
  • 普通授权版买家无需改插件源码,主工程配好 harmony-configs 即可

四、5 分钟快速接入

1. 安装插件

从 DCloud 插件市场导入 keepalive-location,放入项目 uni_modules/keepalive-location制作自定义调试基座后运行。

2. 最小代码示例

import {initTrack,requestPermissions,onLocationUpdate,onStatusChange,startLocation,stopLocation,destroy} from '@/uni_modules/keepalive-location'

// 注册回调(全局注册一次即可)
onLocationUpdate((point) => {
  console.log('定位点', point.latitude, point.longitude, point.accuracy)
})
onStatusChange((status) => {
  console.log('状态变化', status.eventType, status.lastError)
})

// 初始化 → 申请权限 → 开启定位
async function startTrack() {
  await initTrack({
    notificationTitle: '位置服务运行中',
    notificationContent: '正在进行后台定位',
    enableForegroundService: true, // Android 正式包必须 true
    enableCache: true,
    cacheMaxPoints: 2000
  })
  await requestPermissions()
  await startLocation({
    distanceFilter: 0, // 0 = 不按位移过滤
    desiredAccuracy: 10, // 期望精度(米)
    intervalMs: 60000 // 出点间隔,1000–60000ms
  })
}

// 停止
async function stopTrack() {
  await stopLocation()
  destroy()
}
注意:对外初始化方法名为 initTrack,不是 init——避免 iOS/Swift 保留字导致云打包失败。

3. 封装成 Vue 插件(业务层示例)

很多项目会封一层 Service,统一管理出点间隔、上传队列、前后台切换:

// utils/locationPlugin.js 思路
class LocationService {
  async ensureInit() {
    return initTrack({
      notificationTitle: '定位服务通知',
      notificationContent: '正在后台持续定位...',
      enableForegroundService: true,
      enableCache: true
    })
  }

  async startService({ interval = 60 } = {}) {
    await this.ensureInit()
    const perm = checkPermissions()
    if (!perm.permission.location) await requestPermissions()
    await startLocation({
      distanceFilter: 0,
      desiredAccuracy: 10,
      intervalMs: Math.min(Math.max(interval * 1000, 1000), 60000)
    })
  }
}

五、几个实用特性详解

1. 定时出点策略(三端一致)

系统定位只刷新内存中的最近点;插件内部定时器按 intervalMs 回调:

  • 有新 GPS → 用新点
  • 无新 GPS → 复用最近点,更新 timestamp

这样即使司机等红灯、人员站立不动,也能按固定间隔产生轨迹点,适合「每分钟上报一次」的业务需求。

2. 坐标转换(系统 WGS84 → 国内地图)

插件返回的是系统原始 WGS84。上传高德/百度前需转换:

import { toGaode, wgs84ToGcj02 } from '@/uni_modules/keepalive-location'

onLocationUpdate((point) => {
  const gaode = JSON.parse(wgs84ToGcj02(point.latitude, point.longitude))
  // gaode.latitude / gaode.longitude 用于高德地图展示或上报
})
UTS 自定义对象跨 JS 桥可能丢字段,坐标转换结果以 JSON 字符串返回,需 JSON.parse

3. 可选 HTTPS 批量上报

await initTrack({
  enableCache: true,
  upload: {
    url: 'https://your-api.com/track',
    headers: [{ key: 'Authorization', value: 'Bearer xxx' }],
    batchSize: 20,
    intervalMs: 15000,
    includeDeviceId: false
  }
})

上报 JSON 格式:

{
  "deviceId": "optional",
  "platform": "android|ios|harmony",
  "points": [
    {
      "latitude": 0,
      "longitude": 0,
      "accuracy": 0,
      "speed": 0,
      "altitude": 0,
      "timestamp": 0,
      "provider": "gps"
    }
  ]
}

插件不默认向任何第三方服务器上传,只有业务配置了 upload.url 才会 POST。

4. 统一错误码(节选)

错误码 含义
8030001 未授权定位
8030002 无后台定位 / Always
8031004 长时任务取消
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐