【鸿蒙 HarmonyOS Next 最佳实践】位置定位场景开发指导

简介

位置定位提供了GNSS定位、网络定位等多种功能。在实际开发场景中,经常会使用位置定位的功能,如城市定位功能、外卖与快递服务实时跟踪配送员位置、汽车实时导航等等。
常见开发场景如下所示:

  • 使用精准定位确认具体位置
  • 获取历史定位
  • 实时获取当前位置
  • 后台定位

申请定位权限

场景描述

应用在使用位置能力前,首先需要确认系统的位置开关为开启状态。如果系统位置能力没有开启,应用不能使用定位服务。


其次,设备的位置信息需要想用户申请对应的访问权限,用户授权后,应用才能使用定位服务。需要用户授权的用户如下所示。

  • ohos.permission.LOCATION:用于获取精准位置,精准度在米级别。
  • ohos.permission.APPROXIMATELY_LOCATION:用于获取模糊位置,精确度为5公里。
  • ohos.permission.LOCATION_IN_BACKGROUND:用于应用切换到后台仍然需要获取定位信息的场景。

开发步骤

1.在module.json5中配置需要的权限。

{
// ...
 "requestPermissions": [
         {
           "name": "ohos.permission.LOCATION",
           "reason": "$string:location_permission",
           "usedScene": {
             "abilities": [
               "EntryAbility"
             ],
             "when": "inuse"
           }
         },
         {
           "name": "ohos.permission.INTERNET",
           "reason": "$string:internet_permission",
           "usedScene": {
             "abilities": [
               "EntryAbility"
             ],
             "when": "inuse"
           }
         },
         {
           "name": "ohos.permission.APPROXIMATELY_LOCATION",
           "reason": "$string:fuzzy_location_permission",
           "usedScene": {
             "abilities": [
               "EntryAbility"
             ],
             "when": "inuse"
           }
         },
         {
           "name": "ohos.permission.LOCATION_IN_BACKGROUND",
           "reason": "$string:location_permission",
           "usedScene": {
             "abilities": [
               "EntryAbility"
             ],
             "when": "inuse"
           }
         }
       ]
// ...
}



2.详细申请定位权限的代码如下所示。关于权限的申请,详情可以参考向用户申请授权

// 引入abilityAccessCtrl
import { UIAbility, AbilityConstant, Want, abilityAccessCtrl } from '@kit.AbilityKit';
// ...

export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    Logger.info(TAG, `[Demo] EntryAbility onCreate`);
    let atManager = abilityAccessCtrl.createAtManager();
    try {
      // 申请权限
      atManager.requestPermissionsFromUser(this.context,
        ['ohos.permission.INTERNET', 'ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'])
        .then((data) => {
          Logger.info(TAG, `data: ${JSON.stringify(data)}`);
        })
        .catch((err: BusinessError) => {
          Logger.error(TAG, `err: ${JSON.stringify(err)}`);
        })
    } catch (err) {
      Logger.error(TAG, `catch err->${JSON.stringify(err)}`);
    }
  }
// ...
}

使用精准定位确认具体位置

场景描述

关于位置定位,主要有两种方式GNSS定位和网络定位,如下表所示。

定位方式说明优点
GNSS定位基于全球导航卫星系统,包含GPS、GLONASS、北斗、Galileo等,通过导航卫星、设备芯片提供的定位算法,来确定设备准确位置。定位精准
网络定位通过网络进行定位,包括WLAN、蓝牙定位、基站定位。定位速度快

位置定位的策略是主要是基于GNSS定位和网络定位实现的,定位精准的策略使用的是GNSS定位,定位快速的策略使用的网络定位。
获取定位信息的接口getCurrentLocation需要设置定位策略和单次定位超时时间,单次定位超时时间建议设置为10秒,定位策略支持两种参数配置,分别是CurrentLocationRequest和SingleLocationRequest。SingleLocationRequest从API version 12开始支持,在实现上更为简单,也是推荐的实现方式。CurrentLocationRequest和SingleLocationRequest的定位策略对比如下所示:

参数类型策略说明
CurrentLocationRequestPRIORITY_ACCURACY0x501表示精度优先。(GNSS定位)
PRIORITY_LOCATING_SPEED0x502表示快速获取位置优先。(网络定位)
SingleLocationRequestUNSET0x200表示未设置优先级,表示LocationRequestPriority无效。
ACCURACY0x201表示精度优先。(GNSS定位)
LOW_POWER0x202表示低功耗优先。(网络定位)
PRIORITY_LOCATING_SPEED0x203表示快速获取位置优先,如果应用希望快速拿到一个位置,可以将优先级设置为该字段。 快速定位优先策略会同时使用GNSS定位和网络定位技术。
开发步骤

如下流程图所示,使用精准定位确认具体位置分为以下多个步骤。
1.开启系统定位能力,向用户申请位置权限。
2.设置定位策略,确认当前定位的方式。
3.获取当前定位信息。
4.将定位坐标转化为地理描述。

如下代码所示,设置定位的策略为PRIORITY_LOCATING_SPEED,即速度优先,并根据PRIORITY_LOCATING_SPEED的策略调用getCurrentLocation获取当前位置。

getLocationPosition(): void {
  // 设置LocatingPriority定位策略
  let request: geoLocationManager.SingleLocationRequest = {
    locatingPriority: geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
    locatingTimeoutMs: CommonConstants.TEN_THOUSAND
  };
  // 根据策略获取当前定位信息
  geoLocationManager.getCurrentLocation(request).then((location: geoLocationManager.Location) => {
    // 位置转化
    this.getAddress({
      latitude: location.latitude,
      longitude: location.longitude
    });
  }).catch((err: BusinessError) => {
    promptAction.showToast({
      message: JSON.stringify(err),
      duration: CommonConstants.TWO_THOUSAND
    });
  });
}


通过getAddressesFromLocation将定位坐标转化为地理描述,代码如下所示:

async getAddress(location: LocationInter) {
    try {
      // ...
      let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
        locale: getContext(this).resourceManager.getStringSync($r('app.string.language')),
        latitude: location.latitude,
        longitude: location.longitude,
        maxItems: 1
      };
      // 通过getAddressesFromLocation将定位坐标转化为地理描述
      geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, async (err, data) => {
        if (data) {
          this.address = data[0]?.placeName || '';
          this.marker?.setInfoWindowVisible(true)
          this.marker?.setSnippet(this.address)
        } else {
          promptAction.showToast({
            message: JSON.stringify(err),
            duration: CommonConstants.TWO_THOUSAND
          });
        }
      });
    } catch (error) {
      promptAction.showToast({
        message: JSON.stringify(error),
        duration: CommonConstants.TWO_THOUSAND
      });
    }
  }

获取历史定位

场景描述

如下图所示,历史定位的缓存是所有应用公用的,应用1和应用2调用getLastLocation获取的缓存是同一个。

开发步骤

通过getLastLocation获取上一次的缓存定位,代码如下所示。

getPreLocationPosition(): void {
  try {
    let location = geoLocationManager.getLastLocation();
    this.getAddress({
      latitude: location.latitude,
      longitude: location.longitude
    });
  } catch (err) {
    promptAction.showToast({
      message: JSON.stringify(err),
      duration: CommonConstants.TWO_THOUSAND
    });
  }
}

持续获取当前位置

场景描述

持续获取当前位置常用于导航、运动轨迹、出行等场景。持续获取需要设置位置请求参数,当前locationChange事件支持LocationRequest和ContinuousLocationRequest两种参数。ContinuousLocationRequest从API version 12开始支持,简单易用,推荐使用ContinuousLocationRequest进行配置。

开发步骤

持续获取当前位置主要分为以下步骤:

  • 设置位置请求参数ContinuousLocationRequest
  • 开启位置变化订阅locationChange
  • 设置回调接口

核心代码如下所示:

onLocationChange(): void {
  // 设置位置请求参数
  let request: geoLocationManager.ContinuousLocationRequest = {
    interval: 1,
    locationScenario: CommonConstants.NAVIGATION
  };
  try {
    // 开启位置变化订阅
    geoLocationManager.on('locationChange', request, this.locationChange);
  } catch (err) {
    promptAction.showToast({
      message: JSON.stringify(err),
      duration: CommonConstants.TWO_THOUSAND
    });
  }
}
  
// 回调接口
locationChange = (location: geoLocationManager.Location): void => {
  this.getAddress({
    latitude: location.latitude,
    longitude: location.longitude
  });
}

后台定位

场景描述

后台定位常用于应用切换到后台后,应用需要继续定位的场景。后台定位的功能需要获取ohos.permission.LOCATION_IN_BACKGROUND权限,同时需要申请长时任务

核心代码
let context = getContext(this) as common.UIAbilityContext;
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      // 点击通知后,将要执行的动作列表
      wants: [
        {
          bundleName: context.abilityInfo.bundleName,
          abilityName: context.abilityInfo.name
        }
      ],
      operationType: wantAgent.OperationType.START_ABILITY,
      requestCode: 0,
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    }
    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
      backgroundTaskManager.startBackgroundRunning(context, backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj)
        .then(() => {
          console.log('cwq start background task success');
          this.startLocation()
        })
        .catch((err: BusinessError) => {
          console.error(`cwq Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
        });
    }).catch((err: BusinessError) => {
      console.error(`cwq Failed to operation getWantAgent. Code is ${err.code}, message is ${err.message}`);
    });
  }

startLocation() {
    console.log('cwq start location');
    let locationChange = (location: geoLocationManager.Location): void => {
      console.log('cwq locationChanger: data: ' + JSON.stringify(location));
    };
    let requestInfo: geoLocationManager.LocationRequest = {
      'scenario': geoLocationManager.LocationRequestScenario.NAVIGATION,
      'timeInterval': 0,
      'distanceInterval': 0,
      'maxAccuracy': 0
    };
    geoLocationManager.on('locationChange', requestInfo, locationChange);
  }

常见问题

位置定位有偏差
  1. 华为地图在中国大陆、中国香港和中国澳门使用GCJ02坐标系,若使用WGS84坐标系直接叠加在华为地图上,因坐标值不同,展示位置会有偏移。所以,在中国大陆、中国香港和中国澳门如果使用WGS84坐标调用Map Kit服务,需要先将其转换为GCJ02坐标系再访问。
  2. 网络定位的精度较差,可能会有较大偏差。
  3. 如果使用的是GNSS定位,在室内等强遮蔽定位场景下,无法提供准确的位置服务。
先使用getCurrentLocation获取定位,再使用getLastLocation获取定位,两个值不一致

场景描述
用getCurrentLocation获取到的定位后,再使用getLastLocation获取到的位置和原来的值不一样
可能原因
定位缓存所有应用用的都是一份,有可能中间有其它应用定位把缓存位置刷新了。
解决方案
可以对比一下获取的时间,根据时间判断是否有更新。

欢迎大家评论留言,讨论!有其他开发方案也可以分享给我!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值