文档

安装与原生接入

按 React Native、Expo 或 HarmonyOS 选择步骤,让 Release 安装包具备加载更新的能力。

更新于 2026-09-15

本页内容

准备好这些

  • 已经能构建的 React Native、Expo 或 RNOH 工程。命令默认在包含 package.json 的应用根目录执行。

  • Node.js ≥18.17 和工程原有的平台工具链;iOS 构建需要 macOS。

  • 独立测试应用与设备。先跑通一个平台,再扩展。

SDK 声明 React ≥16.8、React Native ≥0.59,不代表所有版本与架构组合都经过验证。保留项目现有版本,根据现有 MainApplication / AppDelegate 文件选择下面的代码。

安装 SDK

先选择工程类型,复制对应命令,在含 package.json 的目录执行。

React Native
npm install -g rn-update-cli
npm install rn-update

如果是 React Native 工程,iOS 还要在 ios 目录执行 pod install(使用 Bundler 时运行 bundle exec pod install),完成后回到应用根目录。Expo 的原生工程在后续构建时生成;首次需要本地原生目录时先运行 npx expo prebuild。

登录并创建或选择应用

在 Pakta 控制台注册账号后,从应用根目录登录:

应用根目录
pakta login

默认情况下,CLI 会把会话凭据保存为当前目录的 .pakta.token,并把 CLI 临时目录 .pakta 加入 .gitignore(如果项目已有 .gitignore)。确认这两个路径没有被提交;不要把密码、会话文件或 PAKTA_API_TOKEN 放入仓库。设置 RNU_SERVICE_URL 连接自托管服务时,凭据会按服务地址隔离保存。

创建应用

Android、iOS、HarmonyOS 是三个独立的平台应用,需要分别创建。创建成功后 CLI 会自动选择该应用,并写入 update.json:

下面每行只执行你要发布的平台,不要把不使用的平台也创建出来。

bash
pakta createApp --platform android --name PaktaDemo
pakta createApp --platform ios --name PaktaDemo
pakta createApp --platform harmony --name PaktaDemo

应用名称可以相同;平台必须对应实际要构建的安装包。

选择已有应用

如果应用已经在控制台创建,先列出目标平台,再执行选择:

bash
pakta apps --platform android
pakta selectApp --platform android

CLI 会展示该平台的应用及其 appId,输入要使用的 appId 即可。iOS 和 HarmonyOS 分别把平台替换为 ios 和 harmony。同一份 update.json 可以逐个平台选择,CLI 只更新当前平台的条目并保留其他条目。

`appId`、`appKey` 与 `update.json`

选择成功后,应用根目录会生成或更新下面这样的文件:

update.json
{
  "android": {
    "appId": "YOUR_ANDROID_APP_ID",
    "appKey": "YOUR_ANDROID_APP_KEY"
  },
  "ios": {
    "appId": "YOUR_IOS_APP_ID",
    "appKey": "YOUR_IOS_APP_KEY"
  },
  "harmony": {
    "appId": "YOUR_HARMONY_APP_ID",
    "appKey": "YOUR_HARMONY_APP_KEY"
  }
}

实际文件只包含已经创建或选择的平台。遗留服务可能写入数字 appId,Pakta 独立服务也可能写入 UUID;两者都是服务端生成的应用身份,不要手动修改、排序或转换。

标识是否敏感用途
appId否CLI、管理 API 和原生包登记时定位应用
appKey公开标识App 内 new Pakta({ appKey }) 连接更新接口
.pakta.token是CLI 本地登录会话,必须加入 .gitignore
PAKTA_API_TOKEN是CI / CLI 发布令牌,只通过 Secret 注入

update.json 不包含发布凭据,可以提交给团队共享。业务代码如果 import 它,内容会进入 JavaScript bundle;只让 CLI 使用时,它只是发布配置。按平台读取 appKey 的示例见根组件接入。

接下来只做与你工程对应的一节。

Android:配置 bundle 加载入口

打开 android/app/src/main/java/你的包名/MainApplication.kt。若文件使用 reactHost 与 getDefaultReactHost,在现有宿主中加入 jsBundleFilePath:

MainApplication.kt · ReactHost
import cn.reactnative.modules.update.UpdateContext

// 保留已有的 ReactHost、PackageList 和 getDefaultReactHost 导入。
override val reactHost: ReactHost by lazy {
  getDefaultReactHost(
    context = applicationContext,
    packageList = PackageList(this).packages,
    jsBundleFilePath = UpdateContext.getBundleUrl(this),
  )
}

若使用 DefaultReactNativeHost / ReactNativeHost,同样导入 UpdateContext,在已有 host 对象内覆盖方法,不再创建第二个宿主:

MainApplication.kt · ReactNativeHost
override fun getJSBundleFile(): String? =
  UpdateContext.getBundleUrl(this@MainApplication)

在现有 android/app/build.gradle 的 release 配置中关闭 PNG 压缩,减少跨构建资源字节差异:

android/app/build.gradle
android {
  buildTypes {
    release {
      crunchPngs false
    }
  }
}

依赖由 React Native 自动链接。构建时间由 SDK 自动生成,保留原有签名和构建设置。

先连接根组件,再从 android 目录运行 ./gradlew assembleRelease;Windows 使用 .\gradlew.bat assembleRelease。有 flavor 时使用对应 Release task。

使用 Java 的工程

打开 android/app/src/main/java/你的包名/MainApplication.java,文件顶部添加 import cn.reactnative.modules.update.UpdateContext;,在已有 ReactNativeHost 或 DefaultReactNativeHost 对象内添加:

MainApplication.java · inside the existing ReactNativeHost
@Override
protected String getJSBundleFile() {
    return UpdateContext.getBundleUrl(MainApplication.this);
}

iOS:Release 使用 Pakta bundle

在 ios 目录安装 Pods:使用 Bundler 时执行 bundle exec pod install,否则执行 pod install。下面按你的 RN 模板选择一组:RN 0.74+ 使用 bundleURL,更早的模板通常使用 sourceURLForBridge:。

AppDelegate.mm · bundleURL
#import "RCTPakta.h"

- (NSURL *)bundleURL
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [RCTPakta bundleURL];
#endif
}

保留现有 Debug 入口、模块名与生命周期。Swift 模板让实际 bundle URL 提供者在 Release 返回 RCTPakta.bundleURL(),通过项目 Objective-C bridging header 暴露 RCTPakta.h,不要整份覆盖 AppDelegate。

完成根组件接入,用 Xcode 打开 .xcworkspace,选择签名与设备,以 Release 构建验证。保留同次归档导出的 IPA 用于登记。

Swift 与旧版 AppDelegate

Swift 工程先在 App target 的 Objective-C bridging header 中添加 #import "RCTPakta.h"。如果没有此文件,新建头文件并在 Build Settings 的 Objective-C Bridging Header 填入相对路径,例如 YourApp/YourApp-Bridging-Header.h。

找到现有提供 bundle URL 的方法,替换 Release 分支。新模板可能把方法放在 ReactNativeDelegate 中,应在那里修改:

AppDelegate.swift · bundleURL
override func bundleURL() -> URL? {
#if DEBUG
  return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
  return RCTPakta.bundleURL()
#endif
}

旧 Objective-C 模板若只有 sourceURLForBridge:,保留方法签名,把其中非 DEBUG 分支改为 return [RCTPakta bundleURL];。混编工程使用 bridge delegate 提供 URL,避免直接用固定文件路径创建 root view。

Expo:构建包含原生模块的 App

Expo Go 不包含 rn-update 原生模块。按顺序操作:

  1. 安装依赖:执行 npx expo install rn-update。需要自定义渠道时,在 expo.plugins 加入内置 Config Plugin。

  2. 连接根布局:在 app/_layout.tsx 用 UpdateProvider 包裹已有 Stack / Slot,客户端在组件外创建。

  3. 构建原生 App:运行 npx expo run:android --variant release,iOS 使用 npx expo run:ios --configuration Release,或项目已有 EAS production profile。

  4. 安装并验证:确认脱离 Metro 可启动;后续生成 OTA 使用 bundle --expo,平台仍为 android / ios。

完整步骤见Expo 接入。

HarmonyOS:按 RNOH 示例接线

HarmonyOS 需要 HAR、ArkTS 与 C++ 注册,不能只安装 npm 包。逐项对照示例:

以下路径假设 node_modules 与 harmony 都在应用根目录。将配置合并到已有文件,保留现有插件和包列表;不要替换整个工程文件。

  1. 给 entry 添加 Pakta HAR 依赖。

  2. 给 entry 的 Hvigor 构建添加 reactNativeUpdatePlugin()。

  3. 在 ArkTS 和 C++ 两处现有包列表中加入 Pakta。

  4. 在 Release 的 RNApp 中优先加载 Pakta 更新,找不到更新时加载内置 bundle.harmony.js。

harmony/entry/oh-package.json5 · dependencies
"pakta": "file:../../node_modules/rn-update/harmony/pakta.har"
harmony/entry/hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { reactNativeUpdatePlugin } from '../../node_modules/rn-update/harmony/hvigor-plugin';

export default {
  system: hapTasks,
  plugins: [reactNativeUpdatePlugin()],
};
harmony/entry/src/main/ets/RNPackagesFactory.ets
import type { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony';
import PaktaPackage from 'pakta';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [new PaktaPackage(ctx)];
}
harmony/entry/src/main/cpp/CMakeLists.txt · after add_library(rnoh_app ...)
set(PAKTA_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules/rn-update/harmony/pakta/src/main/cpp")
target_include_directories(rnoh_app PRIVATE "${PAKTA_CPP_DIR}")
target_sources(rnoh_app PRIVATE "${PAKTA_CPP_DIR}/PaktaTurboModule.cpp")
harmony/entry/src/main/cpp/PackageProvider.cpp
#include "RNOH/PackageProvider.h"
#include "PaktaPackage.h"
using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
  return {std::make_shared<PaktaPackage>(ctx)};
}
harmony/entry/src/main/ets/pages/Index.ets · imports
import { PaktaFileJSBundleProvider } from 'pakta';
import { AnyJSBundleProvider, ResourceJSBundleProvider } from '@rnoh/react-native-openharmony';
Index.ets · RNApp jsBundleProvider
jsBundleProvider: new AnyJSBundleProvider([
  new PaktaFileJSBundleProvider(this.rnohCoreContext.uiAbilityContext),
  new ResourceJSBundleProvider(
    this.rnohCoreContext.uiAbilityContext.resourceManager,
    'bundle.harmony.js'
  ),
]),

运行前确认 this.rnohCoreContext 已就绪。保留工程原有 RNApp 其他参数;Release 不要把 Metro 放在更新文件前面。

应用根目录
pakta bundle --platform harmony --output .pakta/output/harmony.ppk --no-interactive

在 DevEco Studio 同步、签名并构建原生 .app。确认生成的 rawfile bundle 与宿主资源路径一致,安装验证后用 uploadApp 登记。

完整工程在 SDK 示例目录:testHotUpdate、expoUsePakta、harmony_use_pakta。示例的仓库相对路径需要改为你的依赖路径。

自托管:分别配置 SDK 和 CLI

托管服务用户跳过。SDK 连接公开更新接口,CLI 连接管理接口:

加入现有 Pakta 客户端选项
server: {
  main: ['https://YOUR_HOST/api'],
  queryUrls: ['https://YOUR_CDN/endpoints.json'],
},

替换为真实部署地址;端点清单必须属于你的部署。登录 CLI 前设置主机:

PowerShell
$env:RNU_SERVICE_URL = 'https://YOUR_HOST'
Bash / zsh
export RNU_SERVICE_URL=https://YOUR_HOST

React Native 0.60 及以上通常自动链接,先运行 npx react-native config 检查 rn-update 是否被发现,iOS 再安装 Pods。已自动链接时跳过本节,重复注册会出错。

旧工程 Android 需要同时配置 Gradle 依赖和 React Package 注册:

android/settings.gradle
include ':rn-update'
project(':rn-update').projectDir = new File(rootProject.projectDir, '../node_modules/rn-update/android')
android/app/build.gradle · dependencies 内
implementation project(':rn-update')

在 MainApplication.java 的 getPackages() 返回列表中加入 new UpdatePackage(),并添加导入:

MainApplication.java
import cn.reactnative.modules.update.UpdatePackage;

@Override
protected List<ReactPackage> getPackages() {
  return Arrays.<ReactPackage>asList(
    new MainReactPackage(),
    new UpdatePackage()
  );
}

如果工程已经有其他 Package,保留它们并只追加 new UpdatePackage();不要创建第二个 React Host。完成手动链接后,仍需按 Android bundle 入口 配置 UpdateContext.getBundleUrl(...)。

iOS 未自动发现依赖时,在已有 Podfile 的 App target 中添加 pod 'rn-update', :path => '../node_modules/rn-update',再运行 pod install。完成链接后仍需配置前面的 bundle 加载入口。

Pakta 当前不提供可直接加入旧 Xcode 工程的独立 RCTPakta.xcodeproj。RN 0.60 以下且不使用 CocoaPods 的工程,建议先启用 CocoaPods 或升级 React Native;如果必须手动集成,需要把 ios/RCTPakta、共享 C++ 核心、SSZipArchive 依赖和 pakta_build_time.txt 一并加入 App target,完成后仍要验证 Release 启动和原生包登记。

Android 页面恢复:使用 react-native-screens 时

使用 react-native-screens 的工程检查 MainActivity 的恢复配置,避免重载后恢复旧页面状态导致崩溃。下面适用于依赖已提供 RNScreensFragmentFactory 的版本,放在现有 MainActivity 类中,不能放进 MainActivityDelegate:

MainActivity.kt · 添加 import 和 onCreate
import android.os.Bundle
import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory

override fun onCreate(savedInstanceState: Bundle?) {
  supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
  super.onCreate(savedInstanceState)
}

已有 onCreate 时合并到原方法,不添加第二个;旧版本没有该类时,按所用 screens 版本的安装说明处理,不直接导入不存在的类。

已有扫码页面时直接调用parseTestQrCode。想从浏览器或相机打开 App 时,再配置一个你自己使用的 scheme,例如 paktademo。

Android 在已有 MainActivity 的 <activity> 内增加独立 intent-filter,保留原有启动 filter,并确认 activity 使用 android:launchMode="singleTask":

AndroidManifest.xml · MainActivity 内
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="paktademo" />
</intent-filter>

iOS 在 App target 的 Info → URL Types 新增 URL Scheme paktademo,并保留工程现有 React Native Linking 的 AppDelegate 转发。Expo 在 expo.scheme 中设置同一名称。修改后重新构建安装包。

上传测试更新后,从控制台复制完整 hash,打开 paktademo://update?type=__rnPaktaVersionHash&data=实际HASH。Provider 识别后检查该更新;继续按 App 策略下载、生效。testChannel: false 会拒绝测试载荷。

Android AAB 安装包

Google Play 使用 AAB 时,保留实际提交的 AAB,执行 pakta parseAab 路径 检查,再用 pakta uploadAab 路径 登记。不要用另一次构建的 APK 代替。

不要为了接入盲目关闭所有语言、密度、ABI 分割。Pakta CLI 默认从 AAB 提取 base 模块;如果实际分发依赖额外 split,可显式指定:

按实际分发选择 AAB split
pakta uploadAab ./android/app/build/outputs/bundle/release/app-release.aab --splits config.xxhdpi,config.arm64_v8a

也可以使用 --includeAllSplits 生成包含全部 split 的通用登记包。先按实际分发包验证资源和更新;某个 split 缺失资源时,检查资源打包配置及对应原生版本。

下一步

依赖已安装、CLI 已选中正确平台应用、原生入口已配置。前往让 App 检查更新,加上 Provider 与版本面板,再构建安装包。

Expo 本地构建参数参照 Expo CLI 官方文档。