关于作者
白晓明
宁夏图尔科技有限公司董事长兼 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配置文件中声明位置权限
应用程序要想使用位置信息,需要检查是否已经获取用户授权访问设备位置信息,若未获得授权,可以向用户申请需要的位置权限。系统提供的定位权限有:
加入 App 运行在前台,且访问设备当前的精准位置信息,需要在module.json5中申请如下权限,并向用户申请授权,具体可参考https://xie.infoq.cn/article/67f540652c1661c8630ab3678。
{ "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 中可以使用位置服务记录运动轨迹等。当开发者在一个你哟功能程序中使用位置服务时,需要按照约束与限制,确保用户敏感数据的安全。
评论