HarmonyOS 位置服务全攻略:精准定位、地理编码与后台持续定位实现

✨家人们记得点个账号关注,会持续发布大前端领域技术文章💕 🍃

在 HarmonyOS 应用开发中,位置服务是许多场景的核心能力 ------ 无论是本地生活服务的附近推荐、导航应用的实时轨迹追踪,还是出行类 App 的后台定位,都离不开稳定、精准的位置获取能力。本文将从权限配置、位置获取、地理编码转换、持续定位到后台长时定位,一步步拆解 HarmonyOS 位置服务的完整实现流程,附带可直接复用的代码示例,助力开发者快速落地功能。

获取设备的位置信息

1-配置申请位置权限 同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 获取到精准位置,精准度在米级别。

2-检查全局位置开关是否打开 geoLocationManager.isLocationEnabled();

  • 2.1 没开 atManager.requestGlobalSwitch(getContext(this), abilityAccessCtrl.SwitchType.LOCATION) 拉起全局设置位置开关页
  • 2.2 开了 继续

3-获取当前位置 geoLocationManager.getCurrentLocation(request) 可以获取用户经度纬度

4-可以继续通过geoLocationManager.getAddressesFromLocation()地理编码转化为可读性较强地址

  • 获取设备的位置信息,需要有位置权限,位置权限申请
dart 复制代码
// 位置信息(权限组)
{
  "name": "ohos.permission.APPROXIMATELY_LOCATION",
  "reason": '$string:permission_reason_location',
  "usedScene": {}
},
{
  "name": "ohos.permission.LOCATION",
  "reason": '$string:permission_reason_location',
  "usedScene": {}
},

按照步骤申请权限 developer.huawei.com/consumer/cn...

typescript 复制代码
import { geoLocationManager } from '@kit.LocationKit';
import { abilityAccessCtrl, Context, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { permissionUtil } from '../utils/PermissionUtil';

@Entry
@Component
struct Index {
  build() {
    Button('获取位置信息').onClick(async () => {
      // 1 配置文件 申请权限   同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 获取到精准位置,精准度在米级别。
      const state = await permissionUtil.checkPermissions(['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION'], getContext());
      if (!state) return

      // 2 检查全局开关  开了-继续走,没开-弹出让他授权
      const locationEnabled = geoLocationManager.isLocationEnabled();
      console.log('查看当前全局开关状态:', locationEnabled)

      if (!locationEnabled) {  // 没开
        const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
        const state = await atManager.requestGlobalSwitch(getContext(), abilityAccessCtrl.SwitchType.LOCATION)
        if (!state) return  // 拉起全局弹出 用户没授权就终止代码执行
      }

      // 3. 获取位置信息  经度纬度
      geoLocationManager.getCurrentLocation( {
        locatingPriority: geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
        locatingTimeoutMs: 10000
      }).then((result) => { // 调用getCurrentLocation获取当前设备位置,通过promise接收上报的位置
        console.log('current location: ' + JSON.stringify(result));
      })
        .catch((error:BusinessError) => { // 接收上报的错误码
          console.error('promise, getCurrentLocation: error=' + JSON.stringify(error));
        });
      // .....
    })
  }
}

地理编码转化与逆地理编码转化

使用坐标描述一个位置,非常准确,但是并不直观,面向用户表达并不友好。系统向开发者提供了以下两种转化能力。

  • 地理编码转化:将地理描述转化为具体坐标。
  • 逆地理编码转化能力:将坐标转化为地理描述。

其中地理编码包含多个属性来描述位置,包括国家、行政区划、街道、门牌号、地址描述等等,这样的信息更便于用户理解。

开发步骤

  1. 导入geoLocationManager模块,所有与地理编码转化&逆地理编码转化能力相关的功能API,都是通过该模块提供的。
javascript 复制代码
import { geoLocationManager } from '@kit.LocationKit';

2.获取转化结果。

调用getAddressesFromLocation,把坐标转化为地理位置信息。应用可以获得与此坐标匹配的GeoAddress(地理编码地址信息)列表,应用可以根据实际使用需求,读取相应的参数数据。

javascript 复制代码
let reverseGeocodeRequest:geoLocationManager.ReverseGeoCodeRequest = {
  locale: 'zh',
  "latitude": 31.12, "longitude": 121.11, "maxItems": 1
};
try {
    geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, (err, data) => {
        if (err) {
            console.log('getAddressesFromLocation err: ' + JSON.stringify(err));
        } else {
            console.log('getAddressesFromLocation data: ' + JSON.stringify(data));
        }
    });
} catch (err) {
    console.error("errCode:" + JSON.stringify(err));
}

3.示例代码

typescript 复制代码
import { geoLocationManager } from '@kit.LocationKit';
import { abilityAccessCtrl, Context, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { permissionUtil } from '../utils/PermissionUtil';

@Entry
@Component
struct Index {
  build() {
    Button('获取位置信息').onClick(async () => {
      // 1 配置文件 申请权限   同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 获取到精准位置,精准度在米级别。
      const state = await permissionUtil.checkPermissions(['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION'], getContext());
      if (!state) return

      // 2 检查全局开关  开了-继续走,没开-弹出让他授权
      const locationEnabled = geoLocationManager.isLocationEnabled();
      console.log('查看当前全局开关状态:', locationEnabled)

      if (!locationEnabled) {  // 没开
        const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
        const state = await atManager.requestGlobalSwitch(getContext(), abilityAccessCtrl.SwitchType.LOCATION)
        if (!state) return  // 拉起全局弹出 用户没授权就终止代码执行
      }

      // 3. 获取位置信息  经度纬度
      geoLocationManager.getCurrentLocation( {
        locatingPriority: geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
        locatingTimeoutMs: 10000
      }).then((result) => { // 调用getCurrentLocation获取当前设备位置,通过promise接收上报的位置
        console.log('current location: ' + JSON.stringify(result))

        // 正地理编码与逆地理编码
        geoLocationManager.getAddressesFromLocation({"latitude": result.latitude, "longitude": result.longitude, "maxItems": 1}, (err, data) => {
          if (err) {
            console.log('getAddressesFromLocation err: ' + JSON.stringify(err));
          } else {
            console.log('getAddressesFromLocation data: ' + JSON.stringify(data));
          }
        });
        // 正地理编码与逆地理编码 end
      })
        .catch((error:BusinessError) => { // 接收上报的错误码
          console.error('promise, getCurrentLocation: error=' + JSON.stringify(error));
        });
      // .....
    })
  }
}

持续定位

持续定位。多用于导航、运动轨迹、出行等场景。

首先要实例化ContinuousLocationRequest对象,用于告知系统该向应用提供何种类型的位置服务,以及位置结果上报的频率。

  • 设置locationScenario:

建议locationScenario参数优先根据应用的使用场景进行设置,该参数枚举值定义参见UserActivityScenario,例如地图在导航时使用NAVIGATION参数,可以持续在室内和室外场景获取位置用于导航。

  • 设置interval:

表示上报位置信息的时间间隔,单位是秒,默认值为1秒。如果对位置上报时间间隔无特殊要求,可以不填写该字段。

以地图导航场景为例,调用方式如下:

javascript 复制代码
import { geoLocationManager } from '@kit.LocationKit';
let request: geoLocationManager.ContinuousLocationRequest= {
   'interval': 1,
   'locationScenario': geoLocationManager.UserActivityScenario.NAVIGATION
}
let locationCallback = (location:geoLocationManager.Location):void => {
   console.log('locationCallback: data: ' + JSON.stringify(location));
};
try {
   geoLocationManager.on('locationChange', request, locationCallback);
} catch (err) {
   console.error("errCode:" + JSON.stringify(err));
}

如果不主动结束定位可能导致设备功耗高,耗电快;建议在不需要获取定位信息时及时结束定位。

vbnet 复制代码
geoLocationManager.off('locationChange', locationCallback);

完整实例代码

typescript 复制代码
import { geoLocationManager } from '@kit.LocationKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { permissionUtil } from '../utils/PermissionUtil';

@Entry
@Component
struct Index {

  locationCallback(location:geoLocationManager.Location)  {
    console.log('locationCallback: data: ' + JSON.stringify(location));
  }

  aboutToDisappear() {
    geoLocationManager.off('locationChange', this.locationCallback);
  }

  build() {
    Button('获取位置信息').onClick(async () => {
      // 1 配置文件 申请权限   同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 获取到精准位置,精准度在米级别。
      const state = await permissionUtil.checkPermissions(['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION'], getContext());
      if (!state) return

      // 2 检查全局开关  开了-继续走,没开-弹出让他授权
      const locationEnabled = geoLocationManager.isLocationEnabled();
      console.log('查看当前全局开关状态:', locationEnabled)

      if (!locationEnabled) {  // 没开
        const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
        const state = await atManager.requestGlobalSwitch(getContext(), abilityAccessCtrl.SwitchType.LOCATION)
        if (!state) return  // 拉起全局弹出 用户没授权就终止代码执行
      }

      // 3. 获取位置信息  经度纬度 【持续定位。多用于导航、运动轨迹、出行等场景。】
      geoLocationManager.on('locationChange', {
        interval: 1,
        locationScenario: geoLocationManager.UserActivityScenario.NAVIGATION
      }, this.locationCallback);

      // .....
    })
  }
}

后台定位-长时任务

应用退至后台后 ,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起 ,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。在长时任务中可以申请多种类型的任务,并对任务类型进行更新。应用退后台执行业务时,系统会做一致性校验,确保应用在执行相应的长时任务。同时,系统有与长时任务相关联的通知栏消息,用户删除通知栏消息时,系统会自动停止长时任务。

1.需要申请ohos.permission.KEEP_BACKGROUND_RUNNING权限,配置方式请参见声明权限

2.声明后台模式类型。

在module.json5配置文件中为需要使用长时任务的UIAbility声明相应的长时任务类型(配置文件中填写长时任务类型的配置项)。

json 复制代码
 "module": {
     "abilities": [
         {
        			"name": "EntryAbility",
          		 // ...
              // ...
              // 
              "backgroundModes": [
              	// 长时任务类型的配置项
             		"audioRecording"
               	或者
          			"location"   ✅
             ]
         }
     ],
     ...
 }
  1. 创建BackgroundTaskUtil.ets文件封装长短时任务
typescript 复制代码
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common, WantAgent, wantAgent } from '@kit.AbilityKit';

class BackgroundTaskUtil {

  // 长任务-开启
  startLongTask(list:string[], callback: () => void) {  // list任务类型, callback开启成功后走的回调
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      // 点击通知后,将要执行的动作列表
      // 添加需要被拉起应用的bundleName和abilityName
      wants: [
        {
          bundleName: (getContext() as common.UIAbilityContext).abilityInfo.bundleName,
          abilityName: "EntryAbility"
        }
      ],
      // 指定点击通知栏消息后的动作是拉起ability
      actionType: wantAgent.OperationType.START_ABILITY,
      // 使用者自定义的一个私有值
      requestCode: 0,
      // 点击通知后,动作执行属性
      actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
      // 车钥匙长时任务子类型。只有申请bluetoothInteraction类型的长时任务,车钥匙子类型才能生效。
      // 确保extraInfo参数中的Key值为backgroundTaskManager.BackgroundModeType.SUB_MODE,否则子类型不生效。
      // extraInfo: { [backgroundTaskManager.BackgroundModeType.SUB_MODE] : backgroundTaskManager.BackgroundSubMode.CAR_KEY }
    };

    try {
      // 通过wantAgent模块下getWantAgent方法获取WantAgent对象
      wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
        try {
          // let list: Array<string> = ["audioRecording"];
          // let list: Array<string> = ["bluetoothInteraction"]; 长时任务类型包含bluetoothInteraction,CAR_KEY子类型合法
          backgroundTaskManager.startBackgroundRunning(getContext(), list, wantAgentObj).then((res: backgroundTaskManager.ContinuousTaskNotification) => {
            console.info("Operation startBackgroundRunning succeeded");
            // 此处执行具体的长时任务逻辑,如录音,录制等。
            // 写尝试任务
            callback()
          }).catch((error: BusinessError) => {
            console.error(`Failed to Operation startBackgroundRunning. code is ${error.code} message is ${error.message}`);
          });
        } catch (error) {
          console.error(`Failed to Operation startBackgroundRunning. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
        }
      });
    } catch (error) {
      console.error(`Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
    }

  }
  // 长任务-关闭
  stopLongTask() {
    backgroundTaskManager.stopBackgroundRunning(getContext()).then(() => {
      console.info(`Succeeded in operationing stopBackgroundRunning.`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
    });
  }

  // 短任务-开启
  // 短任务-关闭
}

export  const backgroundTaskUtil = new BackgroundTaskUtil()
  • 示例代码
typescript 复制代码
import { geoLocationManager } from '@kit.LocationKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { permissionUtil } from '../utils/PermissionUtil';
import { backgroundTaskUtil } from '../utils/BackgroundTaskUtil';

@Entry
@Component
struct Index {

  locationCallback(location:geoLocationManager.Location)  {
    console.log('locationCallback: data: ' + JSON.stringify(location));
  }

  aboutToDisappear() {
    geoLocationManager.off('locationChange', this.locationCallback);
  }

  build() {
    Button('获取位置信息').onClick(async () => {
      // 1 配置文件 申请权限   同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 获取到精准位置,精准度在米级别。
      const state = await permissionUtil.checkPermissions(['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION'], getContext());
      if (!state) return

      // 2 检查全局开关  开了-继续走,没开-弹出让他授权
      const locationEnabled = geoLocationManager.isLocationEnabled();
      console.log('查看当前全局开关状态:', locationEnabled)

      if (!locationEnabled) {  // 没开
        const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
        const state = await atManager.requestGlobalSwitch(getContext(), abilityAccessCtrl.SwitchType.LOCATION)
        if (!state) return  // 拉起全局弹出 用户没授权就终止代码执行
      }

      // 3. 获取位置信息  经度纬度 【持续定位。多用于导航、运动轨迹、出行等场景。】
      backgroundTaskUtil.startLongTask(['location'], () => {
        geoLocationManager.on('locationChange', {
          interval: 1,
          locationScenario: geoLocationManager.UserActivityScenario.NAVIGATION
        }, this.locationCallback);
      })
      // .....
    })
  }
}

鸿蒙开发者班级

✨家人们点个juejin账号关注,会持续发布大前端领域技术文章💕 🍃

✨家人们点个juejin账号关注,会持续发布大前端领域技术文章💕 🍃

✨家人们点个juejin账号关注,会持续发布大前端领域技术文章💕 🍃

^_^ 点关注、不迷路、主播带你学技术 (๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

相关推荐
IT充电站1 小时前
HarmonyOS 帧动画 animator
harmonyos
IT充电站1 小时前
HarmonyOS 组件导航(Navigation)
harmonyos
6***37941 小时前
HarmonyOS在智能家居中的场景联动
华为·智能家居·harmonyos
u***u6852 小时前
HarmonyOS在智能手表中的开发
华为·harmonyos·智能手表
0***143 小时前
HarmonyOS系统安全机制
华为·harmonyos
m***D2865 小时前
HarmonyOS在智能家居中的家庭网络
华为·智能家居·harmonyos
S***42805 小时前
HarmonyOS在智能家居中的Huawei HiLink
华为·智能家居·harmonyos
anyup5 小时前
🔥100+ 天,已全面支持鸿蒙!uView Pro 近期更新盘点及未来计划
前端·uni-app·harmonyos
lqj_本人6 小时前
鸿蒙Qt触控疑云:事件传递丢失与坐标偏移修复
qt·华为·harmonyos