✨家人们记得点个账号关注,会持续发布大前端领域技术文章💕 🍃
在 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));
});
// .....
})
}
}
地理编码转化与逆地理编码转化
使用坐标描述一个位置,非常准确,但是并不直观,面向用户表达并不友好。系统向开发者提供了以下两种转化能力。
- 地理编码转化:将地理描述转化为具体坐标。
- 逆地理编码转化能力:将坐标转化为地理描述。
其中地理编码包含多个属性来描述位置,包括国家、行政区划、街道、门牌号、地址描述等等,这样的信息更便于用户理解。
开发步骤
- 导入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" ✅
]
}
],
...
}
- 创建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ᵒᵛᵉᵧₒᵤ❤