
【鸿蒙 HarmonyOS Next 最佳实践】位置定位场景开发指导
位置定位提供了GNSS定位、网络定位等多种功能。在实际开发场景中,经常会使用位置定位的功能,如城市定位功能、外卖与快递服务实时跟踪配送员位置、汽车实时导航等等。使用精准定位确认具体位置获取历史定位实时获取当前位置后台定位。
简介
位置定位提供了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的定位策略对比如下所示:
参数类型 | 策略 | 值 | 说明 |
CurrentLocationRequest | PRIORITY_ACCURACY | 0x501 | 表示精度优先。(GNSS定位) |
PRIORITY_LOCATING_SPEED | 0x502 | 表示快速获取位置优先。(网络定位) | |
SingleLocationRequest | UNSET | 0x200 | 表示未设置优先级,表示LocationRequestPriority无效。 |
ACCURACY | 0x201 | 表示精度优先。(GNSS定位) | |
LOW_POWER | 0x202 | 表示低功耗优先。(网络定位) | |
PRIORITY_LOCATING_SPEED | 0x203 | 表示快速获取位置优先,如果应用希望快速拿到一个位置,可以将优先级设置为该字段。 快速定位优先策略会同时使用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);
}
常见问题
位置定位有偏差
- 华为地图在中国大陆、中国香港和中国澳门使用GCJ02坐标系,若使用WGS84坐标系直接叠加在华为地图上,因坐标值不同,展示位置会有偏移。所以,在中国大陆、中国香港和中国澳门如果使用WGS84坐标调用Map Kit服务,需要先将其转换为GCJ02坐标系再访问。
- 网络定位的精度较差,可能会有较大偏差。
- 如果使用的是GNSS定位,在室内等强遮蔽定位场景下,无法提供准确的位置服务。
先使用getCurrentLocation获取定位,再使用getLastLocation获取定位,两个值不一致
场景描述
用getCurrentLocation获取到的定位后,再使用getLastLocation获取到的位置和原来的值不一样
可能原因
定位缓存所有应用用的都是一份,有可能中间有其它应用定位把缓存位置刷新了。
解决方案
可以对比一下获取的时间,根据时间判断是否有更新。
欢迎大家评论留言,讨论!有其他开发方案也可以分享给我!
更多推荐
所有评论(0)