React Native for OpenHarmony 三方库 react-native-app-settings 2.0.1 适配实战:把用户带到当前应用设置页

本文给第一次做 RN 鸿蒙适配的开发者阅读。重点不是“按钮能打开页面”这句结论,而是解释这个库的用途、系统跳转参数如何找到、Promise 到底表示什么,以及如何用最终 tgz 在真机上复现。

适配仓库: oh-react-native/react-native-app-settings
交付分支: main
适配 TAG: 2.0.1-ohos-1.0.1
受测提交: c67a04cf6599307ea511919f996b58551bf7ba1b
配套源码: react-native-app-settings

一、这个库用来干什么

移动应用经常需要把用户带到自己的系统设置页:用户要开启通知、检查权限、清理存储,或者查看版本时,不应该让业务自己仿造一套系统页面。react-native-app-settings 的公开能力只有一个 OpenAppSettings.open(),它的职责就是请求系统打开“当前应用详情”入口。Android 通常使用 ACTION_APPLICATION_DETAILS_SETTINGS,iOS 使用 UIApplicationOpenSettingsURLString;OpenHarmony 的 Settings 应用有自己的 Ability、URI 和参数名,因此不能只复制 Android 的字符串。

这个库的正确理解是“发起系统跳转”,不是“替用户完成设置”。open() 返回 Promise,Promise resolve 只表示系统接受了启动请求,不能说明用户已经打开某个开关,更不能保证用户一定在设置页停留。用户返回 RN 页面后,业务如果关心权限或开关状态,应在 AppState 回到 active 时重新读取自己的状态。

适配前按完整 npm 包名、去 scope 名称、rntpc 前缀检查了 oh-react-native 组织、CPF-RN 组织 和活动中心记录,没有发现同上游的有效适配。上游 2.0.1 使用 Apache-2.0,交付仓库保留原许可证。

在这里插入图片描述

图 1:精简包安装到独立宿主后的首次冷启动,业务页面由最终 HAP 提供。

在这里插入图片描述

图 2:点击“打开应用设置”后,系统进入当前宿主对应的应用详情页。

二、环境和最终交付身份

受测环境为 React Native 0.84.1、React 19.2.3、RNOH 0.84.3、DevEco Studio 26.0.0 Release、HarmonyOS SDK 26.0.0,系统 OpenHarmony-7.0.0.105。文章引用的是受测提交 c67a04cf6599307ea511919f996b58551bf7ba1b 和 TAG 2.0.1-ohos-1.0.1,不是开发目录里后来可能变动的副本。

这是一个带原生系统跳转的 TurboModule。交付仓库保留 index.js、index.harmony.js、NativeOpenAppSettings.ts、Codegen 文件、ArkTS Package、C++ Package、CMake、harmony/react_native_app_settings.har、测试、双语 README、spec.json 和许可证。完整宿主和签名配置属于验证工程,不作为三方库的一部分上传。

三、先看懂调用链,再看代码

调用链如下:

业务按钮
  -> OpenAppSettings.open()
  -> NativeOpenAppSettings TurboModule Spec
  -> C++ Package 方法登记
  -> ArkTS OpenAppSettingsTurboModule
  -> UIAbilityContext.startAbility()
  -> com.huawei.hmos.settings 的 MainAbility

最关键的三个参数来自鸿蒙系统设置应用:目标包为 com.huawei.hmos.settings,Ability 为 MainAbility,URI 为 application_info_entry。want.parameters.settingsParamBundleName 放入当前宿主的 Bundle ID。这里绝不能把示例包名写死进库,否则换一个 RN 应用后所有用户都会被带到错误的应用详情页。

ArkTS 文件位于 harmony/app_settings/index.ets 和对应的 OpenAppSettingsTurboModule.ts。模块初始化时从 UIAbilityContext.abilityInfo.bundleName 取得真实包名,再组装 Want。C++ 侧只负责把 OpenAppSettings 的方法登记给 RNOH,JS Spec 保持 open(): Promise 的异步契约。包名、Package 工厂和 CMake target 必须在 JS、ArkTS、C++ 三层一致,任何一层名字不一致都可能表现为“按钮无反应”。

这项能力不需要网络、存储、定位等敏感权限。跳转由系统 Settings 应用完成,库只发起请求。不同 ROM 的设置页布局可能不同,所以验收应该检查目标应用、名称和版本是否正确,不要依赖某个固定按钮文字或坐标。

四、我实际改了哪些地方

  1. package.json 增加 react-native: ./index,使 Metro 的 HarmonyOS 解析能够找到 index.harmony.js。
  2. index.harmony.js 继续导出上游默认对象,业务侧仍可写 import OpenAppSettings from ‘react-native-app-settings’。
  3. NativeOpenAppSettings.ts 声明 open 为 Promise,并保留原库在 Android、iOS 上的导出,不让鸿蒙分支污染其他平台。
  4. harmony/app_settings/src/main/ets/ 中新增 Package 工厂和 OpenAppSettingsTurboModule.ts,使用 startAbility 发起系统 Want。
  5. harmony/app_settings/src/main/cpp/ 中保留 Codegen 生成的 Package、methodMap_、CMake 和自动链接需要的 HAR 配置。
  6. 交付前从干净目录重建 HAR,确认其中没有宿主 node_modules、旧日志或另一个库的模板文件。

适配时没有把完整 RNOH 宿主上传到库仓库。读者真正需要的是公开入口、Spec、原生实现、HAR 和说明;签名、HAP、缓存及文章证据属于验证工程。这样的目录更容易被其他人作为依赖消费,也能避免把本机绝对路径带到发布包里。

五、用最终 tgz 接入宿主

实际受测的是与文首 TAG 对应的 react-native-app-settings-2.0.1.tgz。把它放入自己的宿主目录后,可复制下面的命令。示例通过环境变量表达读者自己的路径,不依赖作者电脑上的目录。

export RNOH_HOST="$HOME/rnoh-qa"
export EVIDENCE_DIR="$RNOH_HOST/evidence/react-native-app-settings"
mkdir -p "$EVIDENCE_DIR"
cd "$RNOH_HOST"
npm install "$HOME/Downloads/react-native-app-settings-2.0.1.tgz" --save-exact
./node_modules/.bin/react-native link-harmony
cd harmony
ohpm install --all
cd ..
./node_modules/.bin/react-native bundle-harmony --dev false --sourcemap-output "$EVIDENCE_DIR/bundle.map"
hvigorw assembleHap --mode module -p product=default --no-daemon

npm 安装完成后才执行 CLI,避免 npx 临时下载另一套 React Native。link-harmony 会生成自动链接关系;ohpm 安装 HAR 依赖;bundle-harmony 生成 HAP 内的 JS 资源;hvigorw 最后构建签名 HAP。source map 必须确认来自安装后的 node_modules,而不是本地开发 checkout。–dev false 只影响 JS bundle 标志,本轮 HAP 仍是签名 debug 包。

上机时先锁定设备,再安装和启动 HAP:

hdc list targets
export DEVICE_ID="$(hdc list targets | awk 'NF {print $1; exit}')"
hdc -t "$DEVICE_ID" install "$RNOH_HOST/output/tested.hap"
hdc -t "$DEVICE_ID" shell power-shell wakeup
hdc -t "$DEVICE_ID" shell power-shell timeout -o 2147483647
hdc -t "$DEVICE_ID" shell aa start -a EntryAbility -b com.example.rnqa
hdc -t "$DEVICE_ID" shell uitest screenCap -p /data/local/tmp/app-settings.png
hdc -t "$DEVICE_ID" file recv /data/local/tmp/app-settings.png "$EVIDENCE_DIR/app-settings.png"

常亮是验证期间的临时设置,结束后按记录恢复;open() 本身不应修改屏幕超时。

六、业务侧最小用法

下面的页面故意写得简单,目的是让新手看懂 Promise 的边界:

import React, {useEffect, useState} from 'react';
import {AppState, Button, Text, View} from 'react-native';
import OpenAppSettings from 'react-native-app-settings';

export default function SettingsButton() {
  const [message, setMessage] = useState('尚未发起跳转');

  const openSettings = async () => {
    setMessage('正在请求系统设置页...');
    try {
      await OpenAppSettings.open();
      setMessage('系统已接受请求;用户是否修改设置要返回后重新读取');
    } catch (error) {
      setMessage('打开失败,请稍后重试');
    }
  };

  return (
    <View>
      <Button title="打开当前应用设置" onPress={openSettings} />
      <Text>{message}</Text>
    </View>
  );
}

业务侧不能在 await 后直接写“用户已经开启通知”。如果要检查真实结果,可以监听 AppState:

useEffect(() => {
  const sub = AppState.addEventListener('change', state => {
    if (state === 'active') {
      // 在这里重新读取业务关心的权限或开关
    }
  });
  return () => sub.remove();
}, []);

这段逻辑把“启动成功”和“用户操作完成”分开,正是本次适配必须保留的异步语义。

七、真机验证和截图

安装最终 HAP 后先启动 RN 页面,点击按钮,等待系统页面完全出现,再核对 Settings 页面显示的应用名称、版本和包名。返回 RN 后再次点击,检查重复调用和前后台往返。验证没有依赖设置页的坐标,只核对目标应用和跳转结果。

在这里插入图片描述

图 3:第二次进入时仍然指向同一个宿主,说明 Bundle ID 是动态读取的。

在这里插入图片描述

图 4:第三次调用没有创建错误目标,也没有因为重复点击注册长期监听。

在这里插入图片描述

图 5:从 Settings 返回 RN 页面后,业务仍然可以继续运行。

在这里插入图片描述

图 6:强停应用后用第二个进程启动,离线 bundle 中的模块仍能正常加载。

在这里插入图片描述

图 7:冷启动页面作为基线截图,与后续系统页面往返使用同一受测 HAP。

本轮用 mock 测试覆盖了系统接受请求、系统拒绝、上下文异常和重复重试;真机覆盖三次设置页往返和两次不同进程冷启动。受测 HAP SHA-256 为 8cb4f808908eb7c13117e0b3f6310febe527ebbc8f63bfee952ecd055668838d。发布文章只保留可公开访问的仓库和参考文档链接,不引用作者本地日志目录。

八、常见问题和限制

如果页面没有打开,先区分三类问题:第一,Metro 是否真的加载了 index.harmony.js;第二,HAR、C++ Package 和 ArkTS 工厂的模块名是否一致;第三,系统 Settings Ability 是否在当前 ROM 存在。不要一看到页面没跳转就去改 JS 按钮。

Promise resolve 不等于用户完成修改;系统页面布局也不是库的稳定 API。当前实现只保证打开当前应用的详情入口,未承诺所有 OpenHarmony 发行版都有完全相同的 URI,也没有覆盖平板、2in1、企业定制 ROM 或没有 Settings 应用的设备。返回 RN 后的权限刷新必须由业务自行实现。

九、参考链接

欢迎加入 RN for OpenHarmony 社区。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐