#HarmonyOS NEXT 体验官#探索HarmonyOS位置服务:精准定位的科技奥秘 原创 精华
关于作者
白晓明
宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人
华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL
华为开发者学堂/51CTO学堂/CSDN学堂认证讲师
开放原子开源基金会2023开源贡献之星
公众号:开源开发者新视界(openwatcher)
数字化飞速发展的今天,我们所拥有的智能设备中各种各样的应用程序,其中的位置服务功能正悄然改变着我们的生活方式。比如外卖订餐App可根据我们所在位置推荐周边商家和外卖小哥,短视频App可根据我们所在位置推荐周边用户发布的视频。再比如导航类App精准地确定我们的位置,并为我们规划出最佳的出现路线,无论是日常通勤还是陌生城市的探索,都能让我们轻松找到目的地,避免迷路的困扰。当然在紧急情况下,位置服务还可以为救援人员提供准确的位置信息,缩短救援时间。
HarmonyOS位置服务(Location Kit)使用以下多种定位技术提供服务,为用户提供准确地位置信息。
- 坐标:HarmonyOS以1984年世界大地坐标系统为参考,使用经度、纬度数据描述地球上的一个位置。
- GNSS定位:基于全球导航卫星系统,包括GPS、GLONASS、北斗、Galileo等,通过导航卫星、设备芯片提供的定位算法,来确定设备准确的位置。定位过程具体使用哪些定位系统,取决于用户设备的硬件能力。
- 基站定位:根据设备当前驻网基站和相邻基站的位置,估算设备当前位置。此定位方式的定位结果精度较低,并且需要设备可以访问蜂窝网络。
- WLAN/蓝牙定位:根据设备可搜索到的周围WLAN、蓝牙设备位置,估算设备当前位置。此定位方式的定位结果精度依赖于设备周围可见的固定WLAN、蓝牙设备的分布,密度较高时,精度也相较于基站定位方式更高,同时也需要设备可以访问网络。
HarmonyOS位置服务(Location Kit)除了提供基础的定位服务之外,还提供了地理围栏、地理编码、逆地理编码、国家码等功能和接口。
应用程序的位置服务,如同一把双刃剑,既为我们的生活带来了前所未有的便利和精彩,也需要我们在享受其好处的同时,注重个人隐私的保护。在 HarmonyOS 中,当应用程序处于业务场景且需要位置服务(Location Kit)时,系统进行了严格的约束与限制。这一举措旨在保护用户的隐私安全,确保位置信息不被滥用。使用设备位置能力时,需要用户进行确认并主动开启位置开关。若位置开关未开启,系统不会向任何应用提供定位服务。由于设备位置属于用户敏感数据,所以即使用户已经开启位置开关,应用在获取设备位置前仍需要向用户申请位置访问权限。在用户确认允许后,系统才会向应用提供定位服务,如下图所示。
在module.json5
配置文件中声明位置权限
应用程序要想使用位置信息,需要检查是否已经获取用户授权访问设备位置信息,若未获得授权,可以向用户申请需要的位置权限。系统提供的定位权限有:
权限名称 | 权限级别 | 授权方式 | 说明 |
---|---|---|---|
ohos.permission.LOCATION |
normal | user_grant | 允许应用获取设备位置信息。<br>需要与模糊位置权限一起申请。 |
ohos.permission.APPROXIMATELY_LOCATION |
normal | user_grant | 允许应用获取设备模糊位置信息。 |
ohos.permission.LOCATION_IN_BACKGROUND |
normal | user_grant | 允许应用在后台运行时获取设备位置信息。<br>由于安全隐私要求,应用不能通过弹窗的形式被授予后台位置权限,应用如果需要使用后台位置权限,需要引导用户到设置界面手动授予。 |
加入App运行在前台,且访问设备当前的精准位置信息,需要在module.json5
中申请如下权限,并向用户申请授权,具体可参考HarmonyOS应用程序访问控制探究。
{
"module": {
...
"requestPermissions": [
{
"name": "ohos.permission.LOCATION", // 权限名称,为系统已定义的权限
"reason": "$string:location_reason", // 申请权限的原因,当申请权限为user_grant权限时该字段为必填
"usedScene": { // 用于描述权限使用场景,当申请权限为user_grant权限时该字段为必填
"abilities": [
"EntryAbility"
],
"when": "inuse" // 调用时机(inuse:使用时;always:始终)
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
}
}
导入geoLocationManager
模块
geoLocationManager
模块提供GNSS定位、网络定位(蜂窝基站、WLAN、蓝牙定位技术)、地理编码、逆地理编码、国家码和地理围栏等基本功能API。
import { geoLocationManager } from '@kit.LocationKit'
获取当前设备位置
单次获取当前设备位置
多用于查看当前位置、签到打卡、服务推荐等场景。需要实例化SingleLocationRequest
对象,用于告知系统该向应用提供何种类型的位置服务,以及单次定位超时时间。
/**
* 单次定位的请求参数
*/
export interface SingleLocationRequest {
/**
* 优先级信息
*/
locatingPriority: LocatingPriority;
/**
* 超时时间,单位是毫秒
*/
locatingTimeoutMs: number;
}
如果对位置的返回精度要求较高,建议LocatingPriority
参数优先选择PRIORITY_ACCURACY
,会将一段时间内精度较好的结果返回给应用。
如果对定位速度要求较高,建议LocatingPriority
参数优先选择PRIORITY_LOCATING_SPEED
,会将最先拿到的定位结果返回给应用。
/**
* 单次位置请求中的优先级类型
*/
export enum LocatingPriority {
/**
* 精度优先。
* 定位精度优先策略会同时使用GNSS定位和网络定位技术,并将一段时间内精度较好的结果返回给应用。
* 这个时间段长度为SignleLocationRequest.locationTimeoutMs与“30秒”中的较小者。
* 对设备的硬件资源消耗较大,功耗较大。
*/
PRIORITY_ACCURACY = 0x501,
/**
* 快速获取位置优先。
* 快速定位优先策略会同时使用GNSS定位和网络定位技术,以便在室内和户外场景下均可以快速获取到位置结果。
* 对设备的硬件资源消耗较大,功耗也较大。
*/
PRIORITY_LOCATING_SPEED = 0x502
}
以快速定位策略(PRIORITY_LOCATING_SPEED
)为例,调用方式如下:
/**
* 单次获取当前位置信息
* @param priority
* @param timeout
* @returns
*/
static async getSingleLocationRequest(priority: geoLocationManager.LocatingPriority,
timeout: number = 10000): Promise<geoLocationManager.Location | undefined> {
const request: geoLocationManager.SingleLocationRequest = {
locatingPriority: priority,
locatingTimeoutMs: timeout
};
let location: geoLocationManager.Location | undefined = undefined;
try {
location = await geoLocationManager.getCurrentLocation(request);
console.info(`[AppLogger]单次获取当前位置信息:${JSON.stringify(location)}`);
} catch (error) {
const err = error as BusinessError;
console.error(`[AppLogger]单次获取当前位置信息异常:${JSON.stringify(err)}`);
}
return location;
}
Button('单次获取当前位置信息')
.onClick(async () => {
this.location = await LocationUtil.getSingleLocationRequest(geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED);
})
以上是通过实例化SingleLocationRequest
对象来获取当前位置信息,还可以实例化当前位置信息请求参数CurrentLocationRequest
来获取当前位置信息,可以根据业务选择使用合适的实例化对象。
/**
* 当前位置信息请求参数
*/
export interface CurrentLocationRequest {
/**
* 位置请求中位置信息优先级类型。
* 当scenario取值为UNSET时,priority参数生效,否则priority参数不生效。
* 当scenario和priority均取值为UNSET时,无法发起定位请求。
*/
priority?: LocationRequestPriority;
/**
* 位置请求中定位场景类型。
* 当scenario取值为UNSET时,priority参数生效,否则priority参数不生效。
* 当scenario和priority均取值为UNSET时,无法发起定位请求。
*/
scenario?: LocationRequestScenario;
/**
* 精度信息,单位为米。
* 仅在精确位置功能场景(同时授予了ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 权限)下有效,模糊位置功能生效场景(仅授予了ohos.permission.APPROXIMATELY_LOCATION 权限)下该字段无意义。
* 当scenario为NAVIGATION/TRAJECTORY_TRACKING/CAR_HAILING或者priority为ACCURACY时建议设置maxAccuracy为大于10的值。
* 当scenario为DAILY_LIFE_SERVICE/NO_POWER或者priority为LOW_POWER/FIRST_FIX时建议设置maxAccuracy为大于100的值。
* 默认值为0,取值范围为大于等于0。
*/
maxAccuracy?: number;
/**
* 超时时间,单位为毫秒。
*/
timeoutMs?: number;
}
/**
* 位置请求中位置信息优先级类型
*/
export enum LocationRequestPriority {
/**
* 默认优先级,表示未设置优先级,LocationRequestPriority无效。
*/
UNSET = 0x200,
/**
* 精度优先。
* 定位精度优先策略主要以GNSS定位技术为主,会在GNSS提供稳定位置结果之前使用网络定位技术提供服务。
* 在持续定位过程中,如果超过30秒无法获取GNSS定位结果则使用网络定位技术。
*/
ACCURACY,
/**
* 低功耗优先。
* 低功耗定位优先策略仅使用网络定位技术,在室内和户外场景均可提供定位服务。
* 由于其依赖周边基站、可见WLAN、蓝牙设备的分布情况,定位结果精度波动范围较大,推荐在定位结果精度要求不高的场景下使用。
*/
LOW_POWER,
/**
* 快速获取位置优先。
* 快速定位优先策略会同时使用GNSS定位和网络技术定位,以便在室内和户外场景下均可以快速获取到位置结果。
*/
FIRST_FIX
}
/**
* 位置请求中定位场景类型
*/
export enum LocationRequestScenario {
/**
* 默认场景信息,表示未设置场景信息。
* LocationRequestScenario字段无效。
*/
UNSET = 0x300,
/**
* 导航场景。
* 适用于在户外获取设备实时位置的场景,如车载、步行导航。
* 主要是用GNSS定位技术提供定位服务。
*/
NAVIGATION,
/**
* 表示运动轨迹记录场景。
* 适用于记录用户位置轨迹的场景,如运动类应用记录轨迹功能。
* 主要使用GNSS定位技术提供定位服务。
*/
TRAJECTORY_TRACKING,
/**
* 打车场景。
* 适用于用户出行打车时定位当前位置的场景,如网约车类应用。
* 主要使用GNSS定位技术提供定位服务。
*/
CAR_HAILING,
/**
* 日常服务使用场景。
* 适用于不需要定位用户精确位置的使用场景,如新闻资讯、网购、点餐类应用。
* 该场景仅使用网络定位技术提供定位服务。
*/
DAILY_LIFE_SERVICE,
/**
* 无功耗场景。
* 该场景不会主动触发定位,会在其他应用定位时,才给当前应用返回位置。
*/
NO_POWER
}
以快速获取位置(FIRST_FIX
)策略为例,调用代码如下:
/**
* 使用 {CurrentLocationRequest} 实例获取位置信息
* @param priority
* @param scenario
* @param maxAccuracy
* @param timeout
* @returns
*/
static async getCurrentLocationRequest(priority: geoLocationManager.LocationRequestPriority,
scenario: geoLocationManager.LocationRequestScenario,
maxAccuracy: number = 0, timeout: number = 10000): Promise<geoLocationManager.Location | undefined> {
const request: geoLocationManager.CurrentLocationRequest = {
priority: priority,
scenario: scenario,
maxAccuracy: maxAccuracy,
timeoutMs: timeout
};
let location: geoLocationManager.Location | undefined = undefined;
try {
location = await geoLocationManager.getCurrentLocation(request);
console.info(`[AppLogger]单次获取当前位置信息:${JSON.stringify(location)}`);
} catch (error) {
const err = error as BusinessError;
console.error(`[AppLogger]单次获取当前位置信息异常:${JSON.stringify(err)}`);
}
return location;
}
Button('使用CurrentLocationRequest实例获取当前位置信息')
.onClick(async () => {
this.location = await LocationUtil.getCurrentLocationRequest(geoLocationManager.LocationRequestPriority.FIRST_FIX
, geoLocationManager.LocationRequestScenario.UNSET);
})
持续定位
多用于导航、运动轨迹、出行等场景。需要实例化ContinuousLocationRequest
对象,用于告知系统该向应用提供何种类型的位置服务,以及位置结果上报的频率。
/**
* 持续定位的请求参数
*/
export interface ContinuousLocationRequest {
/**
* 上报位置信息的时间间隔,单位为秒。
* 默认为1,取值范围为大于等于0。
* 等于0时对位置上报时间间隔无限制。
*/
interval: number;
/**
* 定位的场景信息。
*/
locationScenario: UserActivityScenario | PowerConsumptionScenario;
}
/**
* 位置请求中的用户活动场景类型
*/
export enum UserActivityScenario {
/**
* 导航场景。
* 适用于在户外获取设备实时位置的场景,如车载、步行导航。
* 主要使用GNSS定位技术提供定位服务。
*/
NAVIGATION = 0x401,
/**
* 运动场景。
* 适用于记录用户位置轨迹的场景,如运动类应用记录轨迹功能。
* 主要使用GNSS定位技术提供定位服务。
*/
SPORT = 0x402,
/**
* 出行场景。
* 适用于用户出行场景,如打车、乘坐公共交通等场景。
* 主要使用GNSS定位技术提供定位服务。
*/
TRANSPORT = 0x403,
/**
* 日常服务使用场景。
* 适用于不需要定位用户精确位置的使用场景,如新闻资讯、网购、点餐类应用。
* 该场景仅使用网络定位技术提供定位服务。
*/
DAILY_LIFE_SERVICE = 0x404
}
/**
* 位置请求中的功耗场景类型
*/
export enum PowerConsumptionScenario {
/**
* 高功耗。
* 以GNSS定位技术为主。
*/
HIGH_POWER_CONSUMPTION = 0x601,
/**
* 低功耗。
* 适用于对用户位置精度要求不高的使用场景,如新闻资讯、网购、点餐类应用。
*/
LOW_POWER_CONSUMPTION = 0x602,
/**
* 无功耗。
* 该场景下不会主动触发定位,会在其他应用定位时,才给当前应用返回位置。
*/
NO_POWER_CONSUMPTION = 0x603
}
以地图导航场景为例,调用方式如下:
/**
* 持续定位
* @param interval
* @param scenario
* @returns
*/
static async getContinuousLocationRequest(interval: number = 0,
scenario: geoLocationManager.UserActivityScenario): Promise<geoLocationManager.Location | undefined> {
const request: geoLocationManager.ContinuousLocationRequest = {
interval,
locationScenario: scenario
};
let locationPromise: Promise<geoLocationManager.Location | undefined> = new Promise(resolve => {
let listener: (data: geoLocationManager.Location) => void;
listener = (data: geoLocationManager.Location) => {
console.info(`[AppLogger]持续定位数据:${JSON.stringify(data)}`);
resolve(data);
}
try {
// 开启位置变化订阅
geoLocationManager.on('locationChange', request, listener);
} catch (error) {
const err = error as BusinessError;
console.error(`[AppLogger]持续定位异常:${JSON.stringify(err)}`);
}
});
return locationPromise;
}
Button('持续定位')
.onClick(async () => {
this.location = await LocationUtil.getContinuousLocationRequest(1, geoLocationManager.UserActivityScenario.NAVIGATION);
})
当然,最后还需要主动结束定位,不然会导致设备功耗高,耗电快,发热等问题。
geoLocationManager.off('locationChange');
总结
位置服务(Location Kit)是应用程序常见的能力之一,如天气预报App中可以使用位置服务查看所在城市的天气、健康运动类App中可以使用位置服务记录运动轨迹等。当开发者在一个你哟功能程序中使用位置服务时,需要按照约束与限制,确保用户敏感数据的安全。