文档
安装配置

请注意,当前版本的api经过了完全重构,与之前的版本(v10.0以下)不兼容。如果你需要查看之前版本的文档,请点击这里 (opens in a new tab)

首先你应该有一个基于 React Native 开发的应用,我们把具有 package.json 的目录叫做你的应用根目录。 如果你还没有初始化应用,请参阅开始使用 React Native (opens in a new tab)

我们假设你已经拥有了开发 React Native 应用的一切环境,包括Node.jsXcodeAndroid SDK等等。

安装

在你的项目根目录下运行以下命令(如果你使用 yarn 等其他包管理器,请自行替换命令):

# 先全局安装命令行工具 npm i -g react-native-update-cli   # 然后在项目目录中安装热更新模块 npm i react-native-update   # 如果没有使用 expo,则进入 iOS 目录安装 iOS 模块 cd ios && pod install  

如果下载极慢或者显示网络失败,请设置使用淘宝镜像 npx nrm use taobao

⚠️

注意请不要混用npm/yarn/pnpm等包管理器及对应的lock文件,团队成员请坚持使用同一包管理器,且仅保留统一格式的lock文件

请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(使用 npx react-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio 中重新编译)才能生效。

手动 link

如果 RN 版本 >= 0.60 则不需要此手动 link 步骤。

⚠️

注意:如果是混编 RN 项目,或monorepo,或任何其他自定义的情况,由于自定义的配置可能不完整或不适应RN的目录结构,导致自动 link 的功能可能不能正常工作。此时即便 RN 版本 >= 0.60,你可能也需要执行手动 link 操作。

iOS

RN < 0.60且使用CocoaPods(推荐)
  1. 在 ios/Podfile 中添加
pod 'react-native-update', path: '../node_modules/react-native-update'
  1. 在项目的 ios 目录下运行pod install
  2. 重新编译
RN < 0.60且不使用CocoaPods
  1. 在 XCode 中的 Project Navigator 里,右键点击LibrariesAdd Files to [你的工程名]
  2. 进入node_modulesreact-native-updateios 并选中RCTPushy.xcodeproj`
  3. 在 XCode 中的 project navigator 里,选中你的工程,在 Build PhasesLink Binary With Libraries 中添加 libRCTPushy.alibz.tbdlibbz2.1.0.tbd
  4. 继续在Build Settings里搜索Header Search Path,添加$(SRCROOT)/../node_modules/react-native-update/ios,勾选recursive
  5. Build Phases添加一个New Run Script Phase运行脚本,内容如下
#!/bin/bash set -x DEST="../node_modules/react-native-update/ios/" date +%s > "$DEST/pushy_build_time.txt"
  1. 尝试编译一下,顺利的话就会在../node_modules/react-native-update/ios/文件夹下面生成一个pushy_build_time.txt文件。然后在Copy Bundle Resources里把生成的pushy_build_time.txt文件添加进去。

Android

RN < 0.60 或其他不能自动 link 的情况
  1. android/settings.gradle中添加如下代码:

    include ':react-native-update' project(':react-native-update').projectDir = new File(rootProject.projectDir,	'../node_modules/react-native-update/android')
  2. android/app/build.gradle的 dependencies 部分增加如下代码:

    implementation project(':react-native-update')
  3. 打开android/app/src/main/java/[...]/MainApplication.java,

  • 在文件开头增加 import cn.reactnative.modules.update.UpdatePackage;
  • getPackages() 方法中增加 new UpdatePackage()(注意上一行可能要增加一个逗号)

配置 Bundle URL

如果你使用 expo 48 或更高版本,且 react-native-update >= 10.28.2,则可以自动配置 bundle url,请直接去往下一个步骤。 如果你没有使用 expo,或 expo 版本低于 48,则需要按以下步骤手动配置。

iOS

在你的 AppDelegate.mm 或 AppDelegate.m 或 AppDelegate.swift 文件(不同 RN 版本可能后缀名不同)中增加如下代码:

⚠️

注意:如果你是混编原生的项目,务必注意不能直接指定 bundleURL 来初始化 rootView,需要先使用initWithDelegate方法来初始化 bridge,然后使用initWithBridge方法来初始化 rootView。否则热更新功能可能无法正常工作。

// ... 其它代码 #import "AppDelegate.h"   #import "RCTPushy.h" // <-- import头文件,注意要放到if条件外面   // 可能项目里有一些条件编译语句,例如有些版本RN自带的flipper // #if DEBUG // 注意**不要**在这里面引入"RCTPushy.h" // #import <FlipperKit/FlipperClient.h> // ... // #endif     // rn 版本 >= 0.74 需要修改 bundleURL 方法 - (NSURL *)bundleURL { #if DEBUG  // 原先DEBUG这里的写法不作修改  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else  return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle #endif }     // rn 版本 < 0.74 需要修改sourceURLForBridge方法 - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG  // 原先DEBUG这里的写法不作修改  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else  return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle #endif }  

Android

在 MainApplication 中增加如下代码(如果是混编原生的项目或其他原因没有使用 ReactApplication,请使用此 api 集成):

// ... 其它代码   // ↓↓↓请注意不要少了这句import import cn.reactnative.modules.update.UpdateContext // ↑↑↑   class MainApplication : Application(), ReactApplication {    override val reactNativeHost: ReactNativeHost =  object : DefaultReactNativeHost(this) {    // ↓↓↓将下面这一段添加到 DefaultReactNativeHost 内部!  override fun getJSBundleFile(): String? {  return UpdateContext.getBundleUrl(this@MainApplication)  }  // ↑↑↑    // ...其他代码  } }

请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(npx react-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio 中重新编译)才能生效。

Harmony

harmony/entry/src/main/cpp/CMakeLists.txt 中增加如下依赖:

add_subdirectory("${OH_MODULES}/pushy/src/main/cpp" ./pushy) target_link_libraries(rnoh_app PUBLIC rnoh_pushy)

harmony/entry/src/main/cpp/PackageProvider.cpp 中增加如下依赖:

#include "RNOH/PackageProvider.h" #include "PushyPackage.h" using namespace rnoh;   std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {  return {  std::make_shared<PushyPackage>(ctx)  }; }

harmony/entry/oh-package.json5 中增加如下依赖:

"dependencies": {  "pushy": "file:../../node_modules/react-native-update/harmony/pushy",   }

harmony/build-profile.json5 中增加如下配置:

 modules: [  // ↓↓↓将下面这一段添加到 modules 内部!  {  name: 'pushy',  srcPath: '../node_modules/react-native-update/harmony/pushy',  },  ],

harmony/hvigor/hvigor-config.json5 中增加如下配置:

{  dependencies: {  pushy: "file:../../node_modules/react-native-update/harmony/pushy",  }, }

harmony/entry/hvigorfile.ts 中增加如下配置:

import { hapTasks } from "@ohos/hvigor-ohos-plugin"; import { reactNativeUpdatePlugin } from "pushy/hvigor-plugin";   export default {  system: hapTasks /* Built-in plugin of Hvigor. It cannot be modified. */,  plugins: [  reactNativeUpdatePlugin(),  ] /* Custom plugin to extend the functionality of Hvigor. */, };

harmony/entry/src/main/ets/RNPackagesFactory.ts 代码如下:

import type {  RNPackageContext,  RNPackage, } from "@rnoh/react-native-openharmony/ts"; import { PushyPackage } from "pushy/ts";   export function createRNPackages(ctx: RNPackageContext): RNPackage[] {  return [new PushyPackage(ctx)]; }

harmony/entry/src/main/ets/pages/Index.ets 中增加如下代码:

// ... 其它代码   // ↓↓↓请注意不要少了这句import import { PushyFileJSBundleProvider } from 'pushy/src/main/ets/PushyFileJSBundleProvider'; // ↑↑↑   @Entry @Component struct Index {  @StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined  @State shouldShow: boolean = false    aboutToAppear(): void {  this.shouldShow = true  }    onBackPress(): boolean | undefined {  // NOTE: this is required since `Ability`'s `onBackPressed` function always  // terminates or puts the app in the background, but we want Ark to ignore it completely  // when handled by RN  this.rnohCoreContext!.dispatchBackPress()    // this.preferences = preferences.getPreferencesSync(this.context, {name:'update'});  return true  }    build() {  Column() {  if (this.rnohCoreContext && this.shouldShow) {  RNApp({  // ... 其他代码  jsBundleProvider: new TraceJSBundleProviderDecorator(  new AnyJSBundleProvider([  // MetroJSBundleProvider.fromServerIp('127.0.0.1'),  // new ResourceJSBundleProvider(rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),  // ↓↓↓将下面这一段添加到 AnyJSBundleProvider 内部!  new PushyFileJSBundleProvider(this.rnohCoreContext.uiAbilityContext),    // 注意无论是否使用 hermes 格式,请保持 bundle 文件名为 bundle.harmony.js  new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')  ]),  this.rnohCoreContext.logger),  })  }  }  .height('100%')  .width('100%')  } }

请记得,任意在 ios 和 android 目录下的修改,一定要重新编译(npx react-native run-ios 或 run-android 命令编译,或在 Xcode/Android Studio 中重新编译)才能生效。

添加测试用的 Deep Link

此步骤可以极其便利的在和用户完全一致的环境中安全地测试热更新,无需额外的设备注册步骤,也不影响现有的热更新流程。

android/app/src/main/AndroidManifest.xml 中添加如下代码:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">  <application>    <!-- 其他代码 -->    <!-- ↓↓↓ 注意 activity 的 launchMode 需要设置为 singleTask -->  <activity  android:launchMode="singleTask">  <!-- ↑↑↑ -->      <!-- ↓↓↓ 添加如下 intent-filter -->  <intent-filter>  <action android:name="android.intent.action.VIEW" />  <category android:name="android.intent.category.DEFAULT" />  <category android:name="android.intent.category.BROWSABLE" />  <!-- 添加你自定义的 scheme -->  <data android:scheme="你自定义的协议名,请不要和常见协议名或其他app冲突" />  </intent-filter>  <!-- ↑↑↑ -->    </activity>  </application> </manifest>

在完成上述配置并编译,并且日后上传了热更包,在后台能看到热更版本之后,你就可以将鼠标移到网页后台的二维码图标上,如下图所示:

Deep Link二维码

勾选使用 Deep Link选项,在后面的输入框中填入你自定义的协议名,在二维码刷新后,即可在设备上直接使用系统相机或扫码功能(注意不能使用微信扫码)来扫描,即可自动触发热更检查功能。

第一次扫码后可能只是触发服务器任务队列,会弹出提示 10 秒后重新扫码,此时会获得热更包,触发内置或自定义的更新策略。

注意如果你使用自定义的更新策略,请务必从useUpdate()中获取updateInfo,而不要依赖checkUpdate方法的返回值,否则扫码不会有后续动作。

禁用 android 的 crunch 优化

android 会在生成 apk 时自动对 png 图片进行压缩,此操作既耗时又影响增量补丁的生成。为了保证补丁能正常生成,您需要在android/app/build.gradle中关闭此操作:

// 在 android/app/build.gradle 文件中   android {  // ...  signingConfigs {  // ...  }  buildTypes {  release {  // ...  // 添加下面这行以禁用crunch  crunchPngs false  }  } }  

禁用 aab 包的资源分割(如果你需要在 Google Play 上架)

如果你需要打 aab 包以在 Google Play 上架,且 react-native-update 版本 低于 10.33.0,则需要禁用 aab 包的资源分割。若已升级到 10.33.0+ 版本,则不需要此设置。

// 在 android/app/build.gradle 文件中   android {  bundle {  density {  // 请禁用资源分割  // 否则热更后可能遇到图片无法显示的问题  // v10.33.0 版本之后不需要此设置  enableSplit = false  }  } }  

登录与创建应用

首先请在 https://pushy-admin.reactnative.cn (opens in a new tab) 注册帐号,然后在你的项目根目录下运行以下命令:

$ pushy login email: <输入你的注册邮> password: <输入你的密>

这会在项目文件夹下创建一个.update文件,注意不要把这个文件上传到 Git 等 CVS 系统上。你可以在.gitignore末尾增加一行.update来忽略这个文件。

登录之后可以创建应用。注意 iOS 平台、安卓平台、鸿蒙平台需要分别创建:

$ pushy createApp --platform ios App Name: <输入应用名> $ pushy createApp --platform android App Name: <输入应用名> $ pushy createApp --platform harmony App Name: <输入应用名>
应用的名字可以相同,这没有关系。

如果你已经在网页端或者其它地方创建过应用,也可以直接选择应用:

$ pushy selectApp --platform ios 1) 鱼多多(ios) 2) 招财旺(ios)   Total 2 ios apps Enter appId: <输入应用前面的编>

选择或者创建过应用后,你将可以在文件夹下看到update.json文件,其内容类似如下形式:

{  "ios": {  "appId": 1,  "appKey": "<一串随机字符串>"  },  "android": {  "appId": 2,  "appKey": "<一串随机字符串>"  },  "harmony": {  "appId": 3,  "appKey": "<一串随机字符串>"  } }

你可以安全的把update.json上传到 Git 等 CVS 系统上,与你的团队共享这个文件,它不包含任何敏感信息。当然,他们在使用任何功能之前,都必须首先输入pushy login进行登录。

至此应用的创建/选择就已经成功了。下一步,你需要给代码添加相应的功能,请参阅代码集成