1. 为什么说鸿蒙上的Geolocation不是改两行配置的事
做 React Native 开发的老手都知道,跨端最舒服的是 UI 那套逻辑,最难受的永远是原生能力。尤其是"持续定位"这种既要权限、又要后台、还要考虑耗电的功能,在 Android/iOS 上都有各种坑,换到鸿蒙上更是如此。React Native for OpenHarmony(后面统一叫 RNOH)这两年成熟了不少,UI 层、基础模块、网络模块基本能跑通,但定位这类偏系统能力的模块,官方覆盖还远没有到"开箱即用"的程度。
这篇文章就基于我最近一个实际项目来聊:在 RNOH 工程里,从 0 到 1 实现 Geolocation 持续定位更新。包含鸿蒙侧原生模块怎么封装、JS 侧怎么订阅、权限怎么声明、后台定位怎么做、以及我在真机上踩过的一串坑。如果你正在做鸿蒙版的 RN 应用,或者准备从 0 搭一个带定位功能的 RNOH 工程,这篇可以直接参考。
1.1 RNOH能给你什么,不能给你什么
先对齐一下 RNOH 的现状。现在网上一搜"react native 鸿蒙",很多教程还在讲怎么跑 Hello World,但实际上 RNOH 已经能支撑相当复杂的企业级应用了。官方脚手架 @react-native-oh-tpl/cli 可以快速初始化工程,react-native-openharmony 这个包提供了 RN 核心库的鸿蒙实现,包括 Metro 打包、组件渲染、基础事件分发、网络请求这些。也就是说,你原来 RN 工程里的大部分 JS 代码,搬到鸿蒙上是可以直接跑的。
但这里有个核心边界:RNOH 提供的是一套运行时和核心模块,不是所有社区 Native 模块都自动有鸿蒙端实现。比如 @react-native-community/geolocation,它在 Android 和 iOS 都有原生实现,但在 RNOH 里你去引用它,直接会报 TurboModuleRegistry.getEnforcing(... 'Geolocation' could not be found) 之类的错,因为在鸿蒙侧根本没有对应的原生模块注册。
所以针对 Geolocation 这件事,我的结论很明确:不要指望社区库,自己写一个鸿蒙侧的 TurboModule,把系统的定位能力桥接给 JS 用,这才是最可控的方案。而且鸿蒙的定位 API 本身设计得比较清爽,封装的工作量比 Android 的 FusedLocationProviderClient 那条链路小很多。
1.2 鸿蒙定位API和Android定位API的差异
鸿蒙定位走的是 @ohos.geoLocationManager,在最新 SDK 里推荐从 @kit.LocationKit 引入。它跟 Android 定位最大的差别有几点:
第一,权限拆分更细。鸿蒙在 API 9 之后把定位权限拆成了 ohos.permission.APPROXIMATELY_LOCATION(模糊位置)和 ohos.permission.LOCATION(精确位置),后台持续定位还要额外声明 ohos.permission.LOCATION_IN_BACKGROUND。Android 上虽然有模糊/精确的概念,但鸿蒙这里是两个独立的权限项,申请逻辑上要分开处理。
第二,定位回调模型不一样。Android 里你一般是 requestLocationUpdates 传入一个 LocationListener;鸿蒙里则是先 on('locationChange', callback) 注册监听,再调用 requestLocationUpdates 去激活定位。停止更新时要同时 off('locationChange'),否则监听还挂在系统那边,容易泄漏。
第三,场景化配置很强。鸿蒙定位请求参数里有 scenario 概念,比如日常生活、导航、运动健康等,系统会根据场景自动调整定位策略。这个对做持续定位来说很重要,后面我会专门讲参数怎么配。
把这两套差异理解透了,再看下面的代码就不会觉得陌生。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 工程准备:RNOH工程、权限声明和真机环境
2.1 搭建RNOH工程
如果你还没有 RNOH 工程,我建议直接用官方脚手架,不要自己手动去拼,否则版本对齐会折腾死你。
bash复制npx @react-native-oh-tpl/cli@latest init RNHarmonyGeoDemo
注意初始化时会有一个选项让你选择 react-native 的版本和 @react-native-openharmony 的版本,这里建议保持默认,除非你有明确的兼容性要求。初始化完成之后,工程结构是标准的 RN + HarmonyOS 双端工程,entry/src/main/ets 目录下面就是鸿蒙侧的 ArkTS 代码。
如果你已有现成的 RNOH 工程,只需要确认一下 oh-package.json5 里已经依赖了 @rnoh/react-native-openharmony,并且版本跟你用的 react-native 版本匹配。我自己遇到过 react-native 0.72 + RNOH 0.72.x 和 0.73.x 的 API 差异,所以强烈建议先看一眼依赖树,别小版本不一致还硬调。
2.2 module.json5权限声明:三件套缺一不可
定位权限在鸿蒙这边属于 user_grant 类型,光在 module.json5 里声明还不够,运行时必须弹出授权弹窗让用户同意。但如果不声明,你连弹窗都触发不了。这一步很容易漏。
我工程的 entry/src/main/module.json5 里是这样声明的:
json复制{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "foreground"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "foreground"
}
},
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "$string:location_background_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "background"
}
}
]
}
}
reason 字段是给用户在授权弹窗里看的说明,不能空着,系统会校验。usedScene 里的 when 表示前台还是后台使用,建议跟前台定位/后台定位的实际情况一一对应。LOCATION_IN_BACKGROUND 如果只做前台定位可以不声明,但做持续定位更新,用户一按 Home 键退后台,定位就会断,所以这个权限最好一起加上。
另外提醒一句:如果你的应用真的是导航或运动类,需要在后台持续跑定位,那还要在 abilities 节点里配置:
json复制{
"name": "EntryAbility",
"backgroundModes": ["location
