在电商类App中,定位功能几乎是刚需:附近门店推荐、区域限购判断、配送地址自动填充,都依赖用户当前位置。但在鸿蒙系统上,定位API的使用并不像文档上写的那么简单,很多坑不实际踩一遍很难发现。这篇文章基于React Native鸿蒙化过程中封装Location TurboModule的实践,梳理@kit.LocationKit中geoLocationManager的用法、权限配置和常见异常。
一、定位API核心步骤
鸿蒙的@kit.LocationKit提供的定位接口主要有四个步骤:检查位置开关、构造请求参数、调用getCurrentLocation、解析Location对象。
1. 位置开关检查
isLocationEnabled()接口不需要权限,只返回系统位置服务是否开启。如果开关未打开,后续的getCurrentLocation会一直等到超时然后抛异常。因此在定位前先做一次检查,可以避免无谓的等待。
- const enabled = geoLocationManager.isLocationEnabled();
- if (!enabled) {
- // 位置服务未开启,直接返回
- }
复制代码
2. 构造SingleLocationRequest
请求参数建议使用新版SingleLocationRequest,其中locatingPriority有三个选项:PRIORITY_LOCATING_SPEED(优先速度)、PRIORITY_ACCURACY(优先精度)、PRIORITY_UNBALANCED_ACCURACY(平衡模式)。实际项目中,如果用户主动点击定位按钮,通常选择速度优先,避免等待过久。
locatingTimeoutMs官方推荐10秒。实测5秒在室内弱信号下经常超时,10秒的成功率明显提高。
- const request: geoLocationManager.SingleLocationRequest = {
- locatingPriority: geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
- locatingTimeoutMs: 10000,
- };
复制代码
3. getCurrentLocation与返回字段
调用getCurrentLocation后,返回的Location对象包含latitude、longitude、altitude、accuracy、speed、direction、timeStamp等字段。在ArkTS侧直接返回给JS层即可。
二、完整实现:从权限到定位
在RNOH(React Native OpenHarmony)环境中,自定义TurboModule通常需要三个部分:JS声明、ArkTS实现、C++桥接。定位模块也不例外。
JS侧声明:
- export interface Spec extends TurboModule {
- getCurrentLocation(): Promise<LocationInfo>;
- }
复制代码
ArkTS侧实现时,需要先申请定位权限。定位权限包括ohos.permission.APPROXIMATELY_LOCATION(模糊,约5公里)和ohos.permission.LOCATION(精确,米级)。两者都是user_grant类型,必须在module.json5中配置reason和usedScene,同时需要在string.json中添加权限说明。只申请模糊权限,系统只会返回5公里精度的位置,无法满足门店级场景。
- {
- "name": "ohos.permission.APPROXIMATELY_LOCATION",
- "reason": "$string:location_reason",
- "usedScene": {
- "abilities": ["EntryAbility"],
- "when": "inuse"
- }
- },
- {
- "name": "ohos.permission.LOCATION",
- "reason": "$string:location_reason",
- "usedScene": {
- "abilities": ["EntryAbility"],
- "when": "inuse"
- }
- }
复制代码
权限弹窗不会自动出现。很多开发者以为调用getCurrentLocation就会触发授权弹窗,实际上必须显式调用abilityAccessCtrl.requestPermissionsFromUser。在TurboModule中,可以通过this.ctx.uiAbilityContext拿到UI上下文,从而发起权限请求。如果用户拒绝,代码直接返回空坐标,JS侧再引导用户去设置页开启。
三、踩坑实录
坑1:位置开关关闭导致超时
实测关闭位置服务后调用getCurrentLocation,等待10秒后抛出异常,错误码不是权限拒绝(201),而是1000500001内部错误。解决方法是前置检查isLocationEnabled,开关关闭时直接返回空结果,避免用户等待。
坑2:模拟器无法定位
DevEco Studio模拟器上isLocationEnabled返回true,但getCurrentLocation一直超时。原因是模拟器默认不提供位置模拟。需要在模拟器控制面板中手动设置模拟经纬度,或者代码中增加降级处理。
坑3:精度不够
PRIORITY_LOCATING_SPEED模式下accuracy通常在50-100米,室内甚至更差。适合作“城市级”判断,但无法判断具体门店。如果需要10米以内精度,需改用PRIORITY_ACCURACY并延长超时到10-15秒,但用户等待体验会变差。
坑4:海拔和方向在某些设备上恒为0
altitude依赖气压计,direction依赖磁力计,低端机型可能没有这些传感器。展示这些字段时要注意,不是代码bug,而是硬件差异。业务逻辑不要依赖altitude做判断。
坑5:权限弹窗不出现
如果不在代码中调用requestPermissionsFromUser,系统不会自动弹窗,getCurrentLocation会直接返回201错误。因此正确的流程是:点击“获取位置”按钮→请求权限弹窗→授权通过→调用定位。
坑6:超时时间设置
locatingTimeoutMs太短在室内几乎必超时,太长又让用户久等。官方推荐10秒,实测室内成功率约85%,户外95%以上。
坑7:异常降级要彻底
定位失败后如果只抛异常,JS层不做处理,页面可能卡住。正确做法是返回默认值(经纬度均为0),页面展示“定位失败”提示,不影响其他功能。
坑8:速度与方向判断
静止时speed和direction为0,但手机轻微震动时speed可能变为0.1或0.2,direction变为随机值。判断“是否在移动”时不要用speed > 0,而应该用speed > 1.0(步行约1.4 m/s)避免误判。
四、业务场景与精度选择
在SkuAssistant场景中,定位主要用来做附近门店推荐和区域限购。拿到坐标后,可以按距离排序门店,或者用坐标判断用户所在城市。定位失败时,返回默认门店列表。
精度选择需要权衡用户体验和耗电。LOCATING_SPEED模式下GPS工作1-3秒,耗电可忽略;ACCURACY模式下GPS长时间工作,耗电明显。如果不是精确导航类应用,建议默认使用SPEED模式。
五、编译与测试注意点
1. import路径必须使用@kit.LocationKit,而不是@ohos.geoLocationManager。老路径在API 14+的RNOH项目中可能解析不到,编译报“Cannot find name 'geoLocationManager'”时先检查这里。
2. 权限配置必须包含reason和usedScene,漏了的话编译器直接报错。
3. 测试建议在真机上开启位置服务。模拟器可以设置模拟位置,但精度与真机不同。
4. 降级逻辑要单独测试:关闭位置服务,确认返回经纬度为0。
5. 修改CMakeLists.txt后必须删除.cxx目录,执行Clean Build,否则会遇到缓存问题。
六、常见问题速查
Q:定位需要哪些权限?
A:需要ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION,都是user_grant类型,需要配置reason和usedScene。
Q:定位精度是多少?
A:SPEED模式50-200米,ACCURACY模式10-50米。
Q:定位失败返回什么?
A:经纬度、时间戳等字段全部返回0,调用方用latitude === 0 && longitude === 0判断。
Q:用户关闭位置开关怎么办?
A:isLocationEnabled返回false,代码直接返回空坐标,不发起定位。
Q:支持持续定位吗?
A:当前只支持单次定位。持续定位需要注册onLocationChange事件,并通过DeviceEventEmitter推送到JS侧,后续版本计划加入。
Q:能否拿到地址描述?
A:可以,使用geocodeManager.getAddressesFromLocation()逆向地理编码,但当前模块未集成。
七、总结
定位模块是几个TurboModule中功能最复杂的一个,涉及权限配置、位置开关检测、超时控制、异常降级等多个环节。与同步常量模块不同,getCurrentLocation是真正的异步API,有超时和错误码,需要仔细处理。建议业务上不要一进App就弹窗要权限,而是在用户主动点击“获取位置”按钮时再申请,授权率会更高。定位失败时不要弹错误提示,直接降级展示默认数据。坐标不应持久化太久,避免跨天使用过期位置。
后续如果出现“持续定位”需求,可以在ArkTS侧注册onLocationChange,通过事件通道将位置数据实时传给JS层。目前单次定位已满足门店推荐和限购判断,更多能力按需再扩展。 |