定位开发:单次/持续定位、省电策略、隐私合规写法

在鸿蒙应用里,定位是高频基础能力:外卖配送、出行打车、天气服务、内容本地化,都离不开位置信息。但很多开发者上手时只关心"怎么拿到经纬度",忽略了权限申请、功耗控制和隐私合规这三件事,最后上架审核被打回,或者用户反馈手机掉电快。本文基于最新鸿蒙 API,把单次定位、持续定位、省电策略和隐私合规的完整落地流程讲清楚,代码可以直接复制到项目里跑。

01 权限前置与隐私合规

定位属于敏感权限,必须先在 module.json5 里声明,再在代码中动态申请。很多人只加了一个 ACCESS_LOCATION,结果持续定位时后台直接被杀,或者上架时权限说明不清晰被驳回。定位相关的权限主要有三个,按业务需要选:

  • ohos.permission.APPROXIMATELY_LOCATION:模糊定位,精度约几公里,适合天气、城市级内容推荐

  • ohos.permission.LOCATION:精确定位,GPS 级精度,适合导航、配送类业务

  • ohos.permission.LOCATION_IN_BACKGROUND:后台定位,应用退到后台后还能持续获取位置

module.json5 里这样配置:

"requestPermissions": [
  {
    "name": "ohos.permission.LOCATION",
    "reason": "$string:location_reason",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  },
  {
    "name": "ohos.permission.APPROXIMATELY_LOCATION",
    "reason": "$string:approx_location_reason",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  },
  {
    "name": "ohos.permission.LOCATION_IN_BACKGROUND",
    "reason": "$string:bg_location_reason",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  }
]

reason 字段必须写清楚为什么要这个权限,系统弹窗和应用市场审核都会展示。模糊定位和精确定位是两个独立权限,用户可以只授权模糊定位,代码里要做兼容处理。

代码里动态申请权限:

import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { bundleManager } from '@kit.BundleKit';

async function requestLocationPermissions(context: common.Context): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager();
  const bundleInfo = await bundleManager.getBundleInfo(context.applicationInfo.name,
    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);

  const tokenId = bundleInfo.appInfo.tokenId;
  const status = await atManager.checkAccessToken(tokenId, 'ohos.permission.LOCATION');

  if (status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
    return true;
  }

  const permissions: string[] = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
  const request = await atManager.requestPermissionsFromUser(context, permissions);
  return request.authResults[0] === 0;
}

这里要注意,requestPermissionsFromUser 是异步弹窗,用户点完才返回结果。不要在页面初始化时直接调用定位接口,权限没拿到会直接抛异常。

02 单次定位:快速获取当前位置

单次定位适合"打开页面拿一次位置"的场景:首页加载附近门店、用户手动刷新位置、提交表单时附带当前坐标。不需要持续监听,拿到结果就可以结束。

鸿蒙定位能力由 @kit.LocationKit 提供,核心接口是 getCurrentLocation。

import { geoLocationManager } from '@kit.LocationKit';

async function getCurrentOnce(): Promise<geoLocationManager.Location> {
  // 先判断系统定位开关是否打开
  const isEnabled = await geoLocationManager.isLocationEnabled();
  if (!isEnabled) {
    throw new Error('系统定位开关未打开');
  }

  const request: geoLocationManager.CurrentLocationRequest = {
    priority: geoLocationManager.LocationRequestPriority.PRIORITY_FAST,
    scenario: geoLocationManager.LocationRequestScenario.UNSET,
    maxAccuracy: 100,
    timeoutMs: 10000
  };

  const location = await geoLocationManager.getCurrentLocation(request);
  console.info('纬度', location.latitude, '经度', location.longitude);
  console.info('定位精度', location.accuracy);
  return location;
}

几个关键参数:

  • priority:PRIORITY_FAST 优先快速返回,PRIORITY_ACCURACY 优先高精度。普通业务用 FAST 就够了,导航类才需要 ACCURACY

  • maxAccuracy:期望精度,单位米。设置 100 表示允许百米级误差,系统会更快返回结果

  • timeoutMs:超时时间。设太短会拿不到结果,设太长用户等得久,一般 10 秒比较合理

单次定位不需要手动停止,系统拿到结果自动释放定位硬件。这也是它省电的原因——用完即走,不会持续占用 GPS。

03 持续定位:实时位置追踪

持续定位适合导航、运动轨迹、实时配送这类场景,需要连续不断地更新位置。实现方式是创建定位订阅,注册回调,不需要的时候主动取消订阅。

import { geoLocationManager } from '@kit.LocationKit';

let subscriber: geoLocationManager.GeofenceSubscribe | null = null;

async function startContinuousLocation() {
  const request: geoLocationManager.LocationRequest = {
    priority: geoLocationManager.LocationRequestPriority.PRIORITY_ACCURACY,
    interval: 5000,
    withdrawCacheAccuracy: 50,
    minDistance: 10
  };

  // 订阅位置变化
  subscriber = await geoLocationManager.createGeofenceSubscribe();
  const callback: geoLocationManager.LocationChangeCallback = (location: geoLocationManager.Location) => {
    console.info('实时位置', location.latitude, location.longitude);
    // 在这里更新 UI、上报服务器
  };

  await subscriber.on('locationChange', request, callback);
}

// 页面离开、任务结束时必须取消订阅
function stopContinuousLocation() {
  if (subscriber) {
    subscriber.off('locationChange');
    subscriber = null;
  }
}

持续定位最容易踩的坑就是忘了取消订阅。页面都退出了,GPS 还在持续定位,电池掉得飞快,用户第二天就卸载了。

正确的做法是把 start 和 stop 绑定到页面生命周期:onPageShow 开始定位,onPageHide 停止定位。如果是后台持续定位(比如外卖配送),要配合后台任务和 LOCATION_IN_BACKGROUND 权限一起用。

04 省电策略:降低定位功耗

定位是耗电大户,尤其是持续开启 GPS 高精度定位。很多开发者为了"定位准",把精度拉满、间隔设成 1 秒,结果手机半小时掉电 20%。实际上,大部分业务不需要这么高精度和高频率。合理的省电策略可以从几个维度入手:

**第一,按场景选定位优先级。**普通列表附近推荐,用 PRIORITY_LOW_POWER,基于网络基站和 WiFi 定位,功耗低,精度几百米就够了。只有导航、打卡这类对精度有硬要求的场景,才切到 PRIORITY_ACCURACY。

**第二,调整定位间隔和最小距离。**interval 设为 5000 毫秒(5 秒),minDistance 设为 10 米——也就是位置变化超过 10 米才触发回调。用户站着不动的时候,不会频繁触发 GPS 唤醒,能省不少电。导航场景可以把间隔缩到 1-2 秒,但非必要不要低于 1 秒。

const lowPowerRequest: geoLocationManager.LocationRequest = {
  priority: geoLocationManager.LocationRequestPriority.PRIORITY_LOW_POWER,
  interval: 30000,
  minDistance: 100
};

**第三,前后台分离策略。**应用在前台时,精度高一点、间隔短一点;退到后台,立刻降低频率和精度。用户切回前台时再恢复。不要不管前后台都用同一套参数。

**第四,页面不可见时暂停定位。**用户切到别的页面、按了 Home 键,当前页面的定位就该停掉。不要因为"万一还要用"就一直挂着,系统定位硬件被持续占用,功耗是线性增长的。

05 落地注意事项

**系统定位开关要判断。**很多用户会手动关掉系统定位开关,这时候无论怎么申请权限都拿不到位置。调用定位接口前,先调 isLocationEnabled 判断一下,如果没开,引导用户去系统设置打开,而不是直接报错。

**模糊定位要兼容。**用户可能只授权了模糊定位,这时候拿到的精度会比较低。业务代码里不要假设精度一定是 GPS 级,要根据 accuracy 字段判断当前定位质量,太低时给用户提示。

**定位回调里不要做重操作。**locationChange 回调频率很高,不要在里面直接做复杂的网络请求、数据库写入。先把位置数据存下来,用节流的方式批量上报,避免频繁 IO 拖慢主线程。

**隐私弹窗要提前说明用途。**权限申请弹窗弹出前,最好有一段引导文案告诉用户"我们为什么需要定位"。直接弹系统权限窗,用户很容易点拒绝,提前解释清楚授权率会高很多。

小结

定位开发的核心不是调 API 拿坐标,而是把权限、功耗、合规三件事配合好。单次定位用完即走,持续定位记得取消订阅;精度和频率按业务场景调,不要上来就拉满;权限申请提前说明用途,模糊定位做兼容。把这套逻辑封装成统一的定位工具类,项目里直接复用,能避免大部分定位相关的功耗投诉和审核驳回。

Logo

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

更多推荐