文档管理中心
最佳实践功能开发应用服务位置定位

位置定位

概述

在实际应用开发过程中,经常需要用到移动终端设备的位置信息,比如查看所在城市天气、出行打车、旅行导航以及观察运动轨迹等。关于位置定位,位置服务提供了两种定位方式,分别为GNSS定位和网络定位,如下表所示:

表1 定位方式介绍
展开

定位方式

说明

优点

GNSS定位

基于全球导航卫星系统,包含GPS、GLONASS、北斗、Galileo等,通过导航卫星、设备芯片提供的定位算法,来确定设备准确位置。

定位精准

网络定位

通过网络进行定位,包括WLAN、蓝牙定位、基站定位。

定位速度快

利用系统的位置定位能力,可以在多种开发场景中获得实时准确的位置信息。本文将介绍如下四种常见的定位场景,并给出其具体实现方案,帮助开发者更好地掌握位置定位的基本原理和开发流程。

  • 当前位置定位:获取设备的当前位置信息。开发者可以根据实际需求将其应用于多种业务场景,如外卖定位、打车定位等。
  • 实时位置定位:持续获取设备的实时位置信息。开发者可以将此能力应用于需要实时定位的场景,如步行导航、驾车出行等。
  • 应用后台定位:将应用切换到后台仍然可以持续获取位置信息。该能力可以用于实现后台应用实时记录运动轨迹等业务场景。
  • 历史定位获取:获取系统缓存的最新位置,即最近一次的历史定位信息。该能力可以用于在设备网络信号较弱或对系统功耗比较敏感的场景下获取位置信息。

当前位置定位

开发者可以根据实际业务诉求,设置相应的定位策略获取设备的当前位置信息,不同定位策略对应表1 定位方式介绍中不同的定位方式。

实现原理

位置服务提供getCurrentLocation()接口来获取当前位置信息,该接口需要用户设置关键参数——定位请求信息。定位请求信息包含定位方式优先级、单次定位超时时间等,分为CurrentLocationRequestSingleLocationRequest两种类型。两种类型对应的定位优先级分别为LocationRequestPriorityLocatingPriority

开发步骤

  1. 申请定位权限,具体内容可参考申请位置权限开发指导应用权限申请
  2. 实例化位置信息请求对象,确认当前定位策略。以实例化SingleLocationRequest对象为例,将其定位方式优先级设置为快速获取位置优先,定位超时时间设置为10秒,具体代码如下:
    收起
    自动换行
    深色代码主题
    复制
    1. let request: geoLocationManager.SingleLocationRequest = {
    2. locatingPriority: 0x502,
    3. locatingTimeoutMs: 10000
    4. };
  3. 根据定位策略,调用getCurrentLocation()接口获取当前位置信息。
    收起
    自动换行
    深色代码主题
    复制
    1. geoLocationManager.getCurrentLocation(request).then((location: geoLocationManager.Location) => {
    2. // ...
    3. }).catch((err: BusinessError) => {
    4. hilog.error(0x0000, TAG, `getCurrentLocationPosition failed, code: ${err.code}, message: ${err.message}`);
    5. // ...
    6. });
  4. 调用getAddressesFromLocation()接口进行逆地理编码转化,将位置坐标信息转换为对应的地理位置描述。
    收起
    自动换行
    深色代码主题
    复制
    1. geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, async (err, data) => {
    2. if (data) {
    3. this.address = data[0]?.placeName || '';
    4. // ...
    5. } else {
    6. hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);
    7. // ...
    8. }
    9. });
  5. 运行效果如下图所示
    图1 获取当前位置信息效果展示

实时位置定位

开发者可以根据实际用户活动场景功耗场景,设置相应的定位策略持续获取设备的位置信息,不同定位策略对应表1 定位方式介绍中不同的定位方式。

实现原理

位置服务通过on('locationChange')接口订阅位置变化情况,实现持续获取设备位置信息的场景诉求。该订阅服务需要申请定位请求信息LocationRequest或者ContinuousLocationRequest,并在请求信息中设置定位场景类型和位置信息上报时间间隔。

开发步骤

  1. 申请定位权限,具体内容可参考申请位置权限开发指导应用权限申请
  2. 实例化位置信息请求对象,确认持续定位策略。以实例化ContinuousLocationRequest为例,将定位场景类型设置为导航场景,位置信息上报时间间隔设置为1秒,具体代码如下:
    收起
    自动换行
    深色代码主题
    复制
    1. let request: geoLocationManager.ContinuousLocationRequest = {
    2. interval: 1,
    3. locationScenario: 0x401
    4. };
  3. 根据定位策略,调用on('locationChange')开启位置变化订阅,并发起定位请求,持续获取当前位置信息。
    收起
    自动换行
    深色代码主题
    复制
    1. geoLocationManager.on('locationChange', request, this.locationChange);
  4. 在on('locationChange')的回调函数中,调用getAddressesFromLocation()接口进行逆地理编码转化,将坐标信息转换为对应的地理位置描述。
    收起
    自动换行
    深色代码主题
    复制
    1. geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, async (err, data) => {
    2. if (data) {
    3. this.address = data[0]?.placeName || '';
    4. // ...
    5. } else {
    6. hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);
    7. // ...
    8. }
    9. });
  5. 在不需要获取位置信息时,及时关闭位置变化订阅,并删除对应的定位请求,减少设备功耗。
    收起
    自动换行
    深色代码主题
    复制
    1. try {
    2. geoLocationManager.off('locationChange', this.locationChange);
    3. } catch (err) {
    4. hilog.error(0x0000, TAG, `offLocationChange failed, code: ${err.code}, message: ${err.message}`);
    5. }
  6. 运行效果如下图所示
    图2 持续获取实时位置信息效果展示

应用后台定位

当用户将应用切至后台且依然需要获取设备的位置信息时,可以使用该方式进行后台定位。

实现原理

应用后台定位需要申请后台定位权限ohos.permission.LOCATION_IN_BACKGROUND和长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING。申请了相关权限后,开启任务模式为定位导航的长时任务,并在其回调接口中通过on('locationChange')订阅位置变化情况,在应用后台持续获取当前位置信息。

开发步骤

  1. 申请后台定位权限,具体内容可参考申请位置权限开发指导应用权限申请
  2. 在模块的module.json5文件中,申请长时任务权限,并将长时任务模式设置为定位导航类型。

    申请长时任务权限

    收起
    自动换行
    深色代码主题
    复制
    1. {
    2. "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
    3. "reason": "$string:running_background",
    4. "usedScene": {
    5. "abilities": [
    6. "EntryAbility"
    7. ],
    8. "when": "always"
    9. }
    10. },

    设置长时任务模式为定位导航类型

    收起
    自动换行
    深色代码主题
    复制
    1. "abilities": [
    2. {
    3. // ...
    4. "backgroundModes": [
    5. "location"
    6. ],
    7. // ...
    8. }
    9. ],
  3. 开启任务模式为定位导航类型的长时任务,在其回调接口中开启位置变化订阅,并发起定位请求,在应用后台持续获取当前位置信息。

    开启长时任务

    收起
    自动换行
    深色代码主题
    复制
    1. startContinuousTask(): void {
    2. let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    3. if (!context) {
    4. return;
    5. }
    6. let wantAgentInfo: wantAgent.WantAgentInfo = {
    7. wants: [
    8. {
    9. bundleName: context.abilityInfo.bundleName,
    10. abilityName: context.abilityInfo.name
    11. }
    12. ],
    13. operationType: wantAgent.OperationType.START_ABILITY,
    14. requestCode: 1,
    15. wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    16. };
    17. wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
    18. backgroundTaskManager.startBackgroundRunning(context,
    19. backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
    20. this.onLocationChange();
    21. hilog.info(0x0000, TAG, 'startBackgroundRunning succeeded');
    22. }).catch((err: BusinessError) => {
    23. hilog.error(0x0000, TAG, `startBackgroundRunning failed, cause: ${JSON.stringify(err)}`);
    24. });
    25. }).catch((err: BusinessError) => {
    26. hilog.error(0x0000, TAG, `getWantAgent failed, cause: ${JSON.stringify(err)}`);
    27. });
    28. }

    订阅位置变化情况

    收起
    自动换行
    深色代码主题
    复制
    1. onLocationChange(): void {
    2. let request: geoLocationManager.ContinuousLocationRequest = {
    3. interval: 1,
    4. locationScenario: 0x401
    5. };
    6. try {
    7. geoLocationManager.on('locationChange', request, this.locationChange);
    8. // ...
    9. } catch (err) {
    10. hilog.error(0x0000, TAG, `onLocationChange failed, code: ${err.code}, message: ${err.message}`);
    11. // ...
    12. }
    13. }
  4. 在位置变化的回调中,调用getAddressesFromLocation()接口进行逆地理编码转化,将坐标信息转换为对应的地理位置描述。
    收起
    自动换行
    深色代码主题
    复制
    1. geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, async (err, data) => {
    2. if (data) {
    3. this.address = data[0]?.placeName || '';
    4. // ...
    5. } else {
    6. hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);
    7. // ...
    8. }
    9. });
  5. 当不需要在应用后台获取位置信息时,及时关闭长时任务和位置变化订阅,并删除对应的定位请求,减少设备功耗。

    关闭长时任务

    收起
    自动换行
    深色代码主题
    复制
    1. stopContinuousTask(): void {
    2. let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    3. if (!context) {
    4. return;
    5. }
    6. backgroundTaskManager.stopBackgroundRunning(context).then(() => {
    7. if (!this.isOnLocationChange) {
    8. this.offLocationChange();
    9. }
    10. hilog.info(0x0000, TAG, 'stopBackgroundRunning succeeded');
    11. }).catch((err: BusinessError) => {
    12. hilog.error(0x0000, TAG, `stopBackgroundRunning failed, cause: ${JSON.stringify(err)}`);
    13. });
    14. }

    关闭位置变化订阅

    收起
    自动换行
    深色代码主题
    复制
    1. offLocationChange(): void {
    2. try {
    3. geoLocationManager.off('locationChange', this.locationChange);
    4. } catch (err) {
    5. hilog.error(0x0000, TAG, `offLocationChange failed, code: ${err.code}, message: ${err.message}`);
    6. }
    7. }
  6. 运行效果如下图所示
图3 在应用后台持续获取位置信息效果展示

历史定位获取

当用户设备网络信号较弱或者对系统功耗比较敏感时,可以先获取系统缓存的最新位置,即最近一次的历史定位信息。

实现原理

位置服务通过getLastLocation()接口来获取系统缓存的最新位置信息。该接口参数列表为空,返回值为Location位置信息。

开发步骤

  1. 申请定位权限,具体内容可参考申请位置权限开发指导应用权限申请
  2. 调用getLastLocation()接口获取系统缓存的最新位置信息。
    收起
    自动换行
    深色代码主题
    复制
    1. let location = geoLocationManager.getLastLocation();
  3. 调用getAddressesFromLocation()接口进行逆地理编码转化,将坐标信息转换为对应的地理位置描述。
    收起
    自动换行
    深色代码主题
    复制
    1. geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, async (err, data) => {
    2. if (data) {
    3. this.address = data[0]?.placeName || '';
    4. // ...
    5. } else {
    6. hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);
    7. // ...
    8. }
    9. });
  4. 运行效果如下图所示
    图4 获取缓存位置信息效果展示

常见问题

位置定位不准或者位置信息有偏差

问题现象

在定位过程中,获取的定位结果不准确,或者将定位结果标记在地图上时出现偏差。

可能原因

  • 定位权限设置错误,例如设置的定位权限为模糊定位而非精准定位。
  • 定位策略设置错误,例如设置的策略为快速获取位置优先(蓝牙、基站、WLAN等网络定位方式)而非精度优先(GNSS卫星定位方式)。
  • 在获取定位结果后,未进行坐标纠偏就将结果标记在地图上。华为地图在中国大陆、中国香港和中国澳门使用的是GCJ02坐标系,而定位返回结果使用的是WGS84坐标系,若直接将结果标记在华为地图上,因坐标值不同,展示位置会有偏移。

解决措施

  • 针对定位权限设置错误问题,需要申请精准定位权限ohos.permission.LOCATION,并在应用中授权获取精准位置。
  • 针对定位策略设置错误问题,需要将定位策略设置为精度优先,采用GNSS卫星定位方式。
  • 针对定位结果在地图上的标记偏差问题,在中国大陆、中国香港和中国澳门如果使用WGS84坐标调用Map Kit服务,需要先将其转换为GCJ02坐标系再访问,详情见坐标纠偏

位置定位失败

问题现象

无法使用定位功能获取位置信息。

可能原因

  • 系统位置开关为关闭状态。
  • 网络信号不佳,导致定位超时。
  • 系统无缓存位置信息,导致获取上一次位置失败。

解决措施

  • 打开系统位置开关。
  • 检查设备是否联网、是否插入SIM卡、WiFi开关是否开启等。
  • 移动至开阔地带再发起定位。
  • 在系统无缓存位置的情况下,使用getCurrentLocation()接口获取当前位置信息。

系统缓存位置信息不准确

问题现象

使用getCurrentLocation()接口获取当前定位信息后,再使用getLastLocation()接口获取缓存定位信息,两次获取的定位信息不一致。

可能原因

所有应用公用系统中的同一份缓存定位信息,有可能在两次接口调用之间有其他应用发起定位,刷新了系统中的缓存定位信息。

解决措施

对比获取定位信息的时间,根据时间判断缓存定位信息是否更新。