目录
- 
一、方案背景与目标 
- 
二、技术选型 
- 
三、集成加载 React Native 
- 
四、混编方案设计(iOS/Android 双端) 
- 
五、洞窝 热更新功能设计 
- 
六、测试与灰度方案 
- 
七、风险与应对 
一、背景与目标
1.1 背景
React Native(RN)具备跨平台、热更新能力。通过将 RN 混编进原生项目,可结合原生项目稳定性与 RN 快速迭代优势,减少应用商店审核依赖,紧急修复成本高的问题。
1.2 目标
一. 先跑通前期流程,RN项目融合到原生,并且实现第三方热更新。
- 
前期跑通RN流程。 (1)搭建RN环境。 (2)创建React Native 项目。 (3)原生新工程空页面配置xcode。 (4)执行bundle exec pod install (后续混合到原生项目中) 
- 
实现 RN 模块与原生项目(iOS/Android)的无缝混编,支持原生调用 RN 页面、RN 调用原生能力。 
- 
原生加载 RN 页面(打包 JS Bundle)使原生项目跑通,并显示RN界面。 
- 
修改RN项目,使其支持pushy热更新,并且加载到原生项目中。 
二. 构建一套热更新系统,支持 RN 业务代码(JS/JSBundle、资源文件)的远程下发、本地更新、版本管理,实现独立可控的热更新能力。
二、技术选型
版本选择依据原生项目最低支持的ios13,所以我们版本是React Native 0.74.5
如果你的项目需要支持更低版本的 iOS(如 iOS 11 及以下),则需要使用 React Native 0.69 或更早版本,但需注意旧版本可能存在安全漏洞和功能限制。
1.RN的版本对比
以下是 React Native 0.74.5、0.75、0.76 及以上版本的核心对比表格,涵盖适配版本、核心优化、兼容性等关键信息,方便直观对比:
| 对比维度 | React Native 0.74.5 | React Native 0.75 | React Native 0.76 及以上 | 
|---|---|---|---|
| 最低 iOS 版本 | iOS 12.4 | iOS 13.4 | iOS 15.1 | 
| 最低 Android 版本 | Android 5.0(API 21) | Android 5.0(API 21) | Android 6.0(API 23) | 
| React 依赖版本 | React 18.2.0 | React 18.2.0 | React 18.2.0(0.76+)/ React 19(未来版本计划) | 
| Hermes 引擎版本 | Hermes 0.18.1 | Hermes 0.19.0(支持 WeakRef 等) | Hermes 0.21.0+(支持 ArrayBuffer.transfer 等) | 
| 核心依赖升级 | Folly 2023.07.24.00 Yoga 2.0.0-rc.5 | Folly 2023.10.02.00 Yoga 2.0.0(稳定版) | Folly 2023.11.27.00+ Yoga 2.1.0+(布局性能优化) | 
| 功能新增 | - 无重大新增 - 修复 TextInput、ScrollView 小问题 | - ScrollView 新增 contentOffset 回调参数 - Pressable 支持 borderless 水波纹 | - Text 组件新增 selectable 属性 - Image 支持 priority 属性(iOS) - Android 分屏模式响应优化 | 
| API 废弃 / 移除 | - 无重大废弃 | - 废弃 SafeAreaView.forceInset - 移除 NavigationExperimental | - 彻底移除 NetInfo 旧 API - 废弃 View.removeClippedSubviews | 
| 开发体验优化 | Metro 0.77.0(基础稳定性修复) | Metro 0.79.1(增量编译提速) | Metro 0.80.5+(TypeScript 5.2+ 支持,内存优化) | 
| 系统适配 | 基础支持 iOS 16、Android 13 | 优化 iOS 16+ 旋转布局、Android 13 通知权限 | 深度适配 iOS 17(Modal 布局修复)、Android 14(媒体权限) | 
| 兼容性范围 | 兼容 iOS 12.4+、Android 5.0+,支持旧设备 | 放弃 iOS 12,聚焦 iOS 13+ 功能优化 | 放弃 iOS 13/14、Android 5.0,仅支持现代系统 | 
| 适用场景 | 需兼容老旧设备(如 iOS 12、Android 5.0)的项目 | 最低支持 iOS 13.4+,需 Hermes 性能提升的项目 | 仅支持新设备(iOS 15.1+、Android 6.0+),追求新特性 | 
2.主要技术选型
| 模块 | 技术选择 | 说明 | 
|---|---|---|
| 混编核心 | React Native(稳定版0.74.5) | 跨平台框架,提供 JS 与原生交互能力 | 
| 原生容器 | iOS(UIViewController)、Android(Activity/Fragment) | 承载 RN 页面的原生容器 | 
| 通信层 | RN 原生模块(Native Modules) | 实现 JS 与原生的方法调用、事件通知 | 
| 热更新核心 | 自定义热更新引擎(基于文件差分) | 替代 pushy,支持 Bundle 下载/校验 | 
| 网络请求 | 原生网络库(iOS: AFNetworking;Android: Retrofit) | 保证热更新包下载的稳定性 | 
| 加密与校验 | SHA256 校验 + AES 加密 | 确保热更新包的完整性与安全性 | 
| 版本管理 | 后端版本接口 + 本地版本记录 | 控制热更新包的下发范围与优先级 | 
三、集成加载 React Native 0.74.5 页面
1.CLI 创建 React Native 项目
安装依赖
使用 Homebrew 安装 Node、Watchman、yarn。(node版本号 v24.10.0)
            
            
              bash
              
              
            
          
          $ brew install node
$ brew install watchman
$ brew install yarn创建 RN 项目
先创建RN 开发页面,具体package.json内容:(可直接复制),放置到空目录cli_rn_modules下面
            
            
              bash
              
              
            
          
          {
  "name": "cli_rn_modules",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-native": "^0.74.5",
    "react-native-safe-area-context": "^5.6.2"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@react-native/babel-preset": "^0.82.1",
    "@react-native/metro-config": "^0.82.1",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "^2.8.8",
    "react-test-renderer": "18.2.0",
    "typescript": "~5.3.3"
  },
  "engines": {
    "node": ">=18"
  }
}安装 React 和 React Native:
            
            
              bash
              
              
            
          
          npm add react@18.2.0 react-native@0.74.5创建个完整的项目(创建不出来也不要紧,结尾会附上demo图)
            
            
              bash
              
              
            
          
          npx react-native initiOS 配置
配置 Xcode
- 
配置 Xcode 证书 
- 
最低版本号设置为 iOS 13.4 
安装 iOS 原生依赖和运行
按照上面 Run instructions for iOS 的操作步骤,安装 iOS Pod 依赖。
            
            
              bash
              
              
            
          
          $ cd ~/my-rn/cli_rn_modules/ios
$ bundle install
$ bundle exec pod install
==================== DEPRECATION NOTICE =====================
Calling `pod install` directly is deprecated in React Native
because we are moving away from Cocoapods toward alternative
solutions to build the project.
* If you are using Expo, please run:
`npx expo run:ios`
* If you are using the Community CLI, please run:
`yarn ios`
=============================================================运行 iOS
            
            
              bash
              
              
            
          
          $ yarn ios截止目前为止RN项目已经完成,并且可以正常打包出来了
            
            
              bash
              
              
            
          
          ### 打包 JS Bundle
| 参数 | 说明 |
|-------|-------|
| --entry-file | JS 入口,通常是 index.js 或 App.js |
| --platform ios | 打包 iOS 版本 |
| --dev false | 生产环境,不包含调试代码 |
| --bundle-output | 打包生成的 JS 文件,放到 OC 项目里 |
| --assets-dest | 打包静态资源(图片、字体) |
``` bash
$ npx react-native bundle \
--entry-file index.js \
--platform ios \
--dev false \
--bundle-output ios/jsbundle/main.jsbundle \
--assets-dest ios/jsbundle/assets2.新建demo的iOS项目集成 React Native
创建目录
先创建 RN 环境文件夹,再创建原生项目子文件夹
            
            
              bash
              
              
            
          
          $ mkdir ./oc_rn_build
$ mkdir ./oc_rn_build/ios安装 NPM 依赖项
进入 oc_rn_build 根目录,还是安装 package.json 文件然后安装 npm 包。
            
            
              bash
              
              
            
          
          {
   "name": "HelloWorld",
   "version": "0.0.1",
   "private": true,
   "scripts": {
      "android": "react-native run-android",
      "ios": "react-native run-ios",
      "lint": "eslint .",
      "start": "react-native start",
      "test": "jest"
   },
   "dependencies": {
      "react": "^18.2.0",
      "react-native": "0.74.5",
      "react-native-safe-area-context": "^5.6.2"
   },
   "devDependencies": {
      "@babel/core": "^7.20.0",
      "@babel/preset-env": "^7.20.0",
      "@babel/runtime": "^7.20.0",
      "@react-native/babel-preset": "^0.82.1",
      "@react-native/metro-config": "^0.82.1",
      "@types/react": "^18.2.6",
      "@types/react-test-renderer": "^18.0.0",
      "babel-jest": "^29.6.3",
      "eslint": "^8.19.0",
      "jest": "^29.6.3",
      "prettier": "^2.8.8",
      "react-test-renderer": "18.2.0",
      "typescript": "~5.3.3"
   },
   "engines": {
      "node": ">=18"
   }
}cd ~/oc_rn_build:然后执行:
            
            
              bash
              
              
            
          
          bundle install
bundle exec pod install 注意点:一定要将 Hermes 版本改为 0.74.5
配置 CocoaPods
将原生项目根目录所有文件放在 ./oc_rn_build/ios 子文件夹。配置 CocoaPods 需要两个文件:
- 
Gemfile文件在根目录./oc_rn_build下载
- 
Podfile文件在子目录./oc_rn_build/ios下载
            
            
              bash
              
              
            
          
          $ curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.75-stable/template/Gemfile
$ cd ./ios
$ curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.75-stable/template/ios/Podfile可以修改 Gemfile 文件
            
            
              bash
              
              
            
          
          -gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
+gem 'cocoapods', '1.16.2'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
-gem 'xcodeproj', '< 1.26.0'
+gem 'xcodeproj', '1.27.0'配置 Xcode
- 
配置 Xcode 证书 
- 
最低版本号设置为 iOS 13.4 - React Native 0.74.5.0 最低支持 iOS 13.4
 
- 
禁用 Xcode 的用户脚本沙盒 - 
打开 Xcode 项目 
- 
Build Settings 搜索 ENABLE_USER_SCRIPT_SANDBOXING
- 
将该选项的值设置为 NO
 
- 
- 
禁用 "基于视图控制器" 的控制方式,强制使用全局设置 - 
打开 Xcode 项目 
- 
Info 添加 UIViewControllerBasedStatusBarAppearance设置为NO
 
- 
集成 RN 环境
先安装 Ruby 依赖项,然后集成 RN 环境。
            
            
              bash
              
              
            
          
          $ cd ./ios
$ bundle install
$ bundle exec pod install3.iOS 加载 RN 页面
打包 JS Bundle
在 RN 项目的根目录打包 jsbundle 拷贝到原生项目的 jsbundle。
            
            
              bash
              
              
            
          
          $ cd ./cli_rn_modules
$ npx react-native bundle \
--entry-file index.js \
--platform ios \
--dev false \
--bundle-output ios/jsbundle/main.jsbundle \
--assets-dest ios/jsbundle/assets
$ cp -Rf ./cli_rn_modules/ios/jsbundle ./oc_rn_build/ios| 参数 | 说明 | 
|---|---|
| --entry-file | JS 入口,通常是 index.js 或 App.js | 
| --platform ios | 打包 iOS 版本 | 
| --dev false | 生产环境,不包含调试代码 | 
| --bundle-output | 打包生成的 JS 文件,放到 OC 项目里 | 
| --assets-dest | 打包静态资源(图片、字体) | 
原生跳转 RN 页面
使用 Xcode 将 jsbundle 拖入项目工程,创建 ReactNativeFactoryDelegate 类用于初始化 RCTReactNativeFactory。
            
            
              Objective-C
              
              
            
          
          // ReactViewController.m
#import "ReactViewController.h"
#import "ReactNativeFactoryDelegate.h"
#import <RCTReactNativeFactory.h>
#import <RCTAppDependencyProvider.h>
@interface ReactViewController ()
@property (nonatomic, copy) NSString *moduleName;
@end
@implementation ReactViewController {
    RCTReactNativeFactory *_factory;
    id<RCTReactNativeFactoryDelegate> _factoryDelegate;
}
- (instancetype)initWithModuleName:(NSString *)moduleName {
    self = [super init];
    if (self) {
        _moduleName = [moduleName copy];
    }
    return self;
}
- (instancetype)init {
    return [self initWithModuleName:@"cli_rn_modules"]; // 默认模块名
}
- (void)viewDidLoad {
    [super viewDidLoad];
    _factoryDelegate = [ReactNativeFactoryDelegate new];
    _factoryDelegate.dependencyProvider = [RCTAppDependencyProvider new];
    _factory = [[RCTReactNativeFactory alloc] initWithDelegate:_factoryDelegate];
    // 创建React Native视图,使用指定的模块名
    UIView *rootView = [_factory.rootViewFactory viewWithModuleName:self.moduleName];
    self.view = rootView;
}
@end模块名称 moduleName 在 RN 项目 cli_rn_modules/index.js 文件注册页面。
            
            
              Objective-C
              
              
            
          
          // 注册页面
AppRegistry.registerComponent(appName, () => App);
AppRegistry.registerComponent('HomePage', () => Home);
AppRegistry.registerComponent('MyPage', () => My);
AppRegistry.registerComponent('SettingsPage', () => Settings);
AppRegistry.registerComponent('BusinessCardView', () => BusinessCardView);
...4. 常见问题
- 
React Native 中文网没有 0.82.0 版本的文档 
- 
yarn install 报错 - 
如果是网络原因请检查 VPN 
- 
如果是 node 版本问题,请检查电脑 brew、nvm、npm 管理的 node 版本,更新到报错信息要求的版本 
 
- 
- 
bundle exec pod install 后 Xcode 运行项目 Hermes 报错 - Build Settings 搜索 ENABLE_USER_SCRIPT_SANDBOXING,将该选项的值设置为NO
 
- Build Settings 搜索 
- 
原生跳转 RN 页面弹出报错弹窗 - Info 添加 UIViewControllerBasedStatusBarAppearance 设置为 NO
 
四、混编方案设计(iOS/Android 双端)
4.1 项目结构设计
采用「原生项目为主,RN 模块作为子模块」的结构,避免对原生项目的侵入性改造:
            
            
              bash
              
              
            
          
          原生项目根目录/
├── ios/                 # iOS 原生代码
├── android/             # Android 原生代码
├── rn_module/           # RN 子模块
│   ├── src/             # RN 业务代码(组件、页面)
│   ├── index.js         # RN 入口文件
│   ├── metro.config.js  # RN 打包配置
│   └── package.json     # RN 依赖
└── hotupdate/           # 热更新相关原生代码(双端共用逻辑抽象)4.2 iOS 混编实现
4.2.1 集成 RN 环境
- 
通过 CocoaPods 引入 RN 核心库( React.podspec、yoga.podspec等),指定版本与原生项目兼容。
- 
在 Podfile中添加 RN 子模块依赖,确保原生项目可访问 RN 代码:
ruby
            
            
              bash
              
              
            
          
          # Podfile 示例 target 'NativeProject' do pod 'React', :path => '../rn_module/node_modules/react-native/' pod 'yoga', :path => '../rn_module/node_modules/react-native/ReactCommon/yoga'# 其他 RN 依赖 end4.2.2 RN 页面容器
自定义 RNViewController,继承 UIViewController,通过 RCTRootView 加载 RN 页面:
            
            
              bash
              
              
            
          
          objective-c
`// RNViewController.h
#import <UIKit/UIKit.h>
#import <React/RCTRootView.h>
@interface RNViewController : UIViewController
/// 初始化 RN 页面容器
/// @param moduleName RN 入口组件名称
/// @param initialProps 传递给 RN 的初始参数
● (instancetype)initWithModuleName:(NSString *)moduleName 
 initialProps:(NSDictionary *)initialProps;
@end`
objective-c
`// RNViewController.m
#import "RNViewController.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
@implementation RNViewController
● (instancetype)initWithModuleName:(NSString *)moduleName 
 initialProps:(NSDictionary *)initialProps {
if (self = [super init]) {
 // 初始化 RN 桥接器
 RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self 
 launchOptions:nil];
 // 创建 RN 根视图
 RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
 moduleName:moduleName
 initialProperties:initialProps];
 self.view = rootView;
}
return self;
}
pragma mark - RCTBridgeDelegate
(NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
// 开发环境加载本地服务,生产环境加载本地 bundle
#ifdef DEBUG
 return [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
#else
 return [[NSBundle mainBundle] URLForResource:@"index.ios" withExtension:@"bundle"];
#endif 
}
@end`使用示例:原生页面打开 RN 页面
objective-c
            
            
              bash
              
              
            
          
          // 在原生按钮点击事件中调用 RNViewController *rnVC = [[RNViewController alloc] initWithModuleName:@"HomePage" initialProps:@{@"userId": @"10086"}]; [self.navigationController pushViewController:rnVC animated:YES];4.2.3 原生与 RN 通信
- 原生模块暴露 :创建 RNBridgeModule继承RCTEventEmitter,暴露原生能力给 RN:
            
            
              bash
              
              
            
          
          objective-c
`// RNBridgeModule.h
#import <React/RCTEventEmitter.h>
@interface RNBridgeModule : RCTEventEmitter 
@end`
objective-c
`// RNBridgeModule.m
#import "RNBridgeModule.h"
#import <UIKit/UIKit.h>
@implementation RNBridgeModule
// 模块名称(RN 中通过此名称调用)
RCT_EXPORT_MODULE();
// 暴露获取设备信息的方法给 RN
RCT_EXPORT_METHOD(getDeviceInfo:(RCTResponseSenderBlock)callback) {
 NSString *deviceModel = [UIDevice currentDevice].model;
 NSString *systemVersion = [UIDevice currentDevice].systemVersion;
 callback(@[[NSNull null], @{@"model": deviceModel, @"systemVersion": systemVersion}]);
}
// 原生主动发送事件给 RN(例如网络状态变化)
● (void)networkStatusChanged:(NSString *)status {
[self sendEventWithName:@"onNetworkStatusChange" body:@{@"status": status}];
}
// 注册事件名称(RN 需要监听的事件)
● (NSArray<NSString *> *)supportedEvents {
return @[@"onNetworkStatusChange"];
}
@end`
● RN 调用原生:
javascript
运行
`import { NativeModules } from 'react-native';
// 调用原生方法
NativeModules.RNBridgeModule.getDeviceInfo((error, result) => {
 if (error) {
 console.error('获取设备信息失败:', error);
 } else {
 console.log('设备信息:', result);
 }
});`
● RN 监听原生事件:
javascript
运行
`import { NativeEventEmitter } from 'react-native';
const eventEmitter = new NativeEventEmitter(NativeModules.RNBridgeModule);
const subscription = eventEmitter.addListener(
 'onNetworkStatusChange',
 (status) => {
 console.log('网络状态变化:', status);
 }
);
// 组件卸载时移除监听
subscription.remove();`4.3 Android 混编实现
4.3.1 集成 RN 环境
- 
在 settings.gradle中引入 RN 子模块:gradle // settings.gradle include ':app', ':rn_module' project(':rn_module').projectDir = new File(rootProject.projectDir, '../rn_module/android')
- 
在 app/build.gradle中添加依赖:
gradle
// app/build.gradle dependencies { implementation project(':rn_module') implementation "com.facebook.react:react-native:+" // 与 RN 版本匹配 // 其他依赖 }
4.3.2 RN 页面容器
自定义 RNActivity 继承 AppCompatActivity,通过 ReactRootView 加载 RN 页面:
            
            
              bash
              
              
            
          
          `// RNActivity.java
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
public class RNActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
 private ReactRootView mReactRootView;
 private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 获取从原生传递的参数
    String moduleName = getIntent().getStringExtra("moduleName");
    Bundle initialProps = getIntent().getExtras();
    // 初始化 RN 根视图
    mReactRootView = new ReactRootView(this);
    mReactInstanceManager = ReactInstanceManager.builder()
            .setApplication(getApplication())
            .setBundleAssetName("index.android.bundle") // 内置 bundle 名称
            .setJSMainModulePath("index")
            .addPackage(new MainReactPackage()) // 注册基础包
            .addPackage(new CustomReactPackage()) // 注册自定义模块
            .setUseDeveloperSupport(BuildConfig.DEBUG)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build();
    // 启动 RN 页面
    mReactRootView.startReactApplication(mReactInstanceManager, moduleName, initialProps);
    setContentView(mReactRootView);
}
// 处理返回键事件
@Override
public void onBackPressed() {
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onBackPressed();
    } else {
        super.onBackPressed();
    }
}
@Override
public void invokeDefaultOnBackPressed() {
    super.onBackPressed();
}
// 生命周期同步
@Override
protected void onPause() {
    super.onPause();
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostPause(this);
    }
}
@Override
protected void onResume() {
    super.onResume();
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostResume(this, this);
    }
}
@Override
protected void onDestroy() {
    super.onDestroy();
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostDestroy(this);
    }
    if (mReactRootView != null) {
        mReactRootView.unmountReactApplication();
    }
}
}`使用示例:原生页面打开 RN 页面
// 在原生按钮点击事件中调用 Intent intent = new Intent(this, RNActivity.class); intent.putExtra("moduleName", "HomePage"); intent.putExtra("userId", "10086"); startActivity(intent);
4.3.3 原生与 RN 通信
- 自定义原生模块 :创建 RNBridgeModule继承ReactContextBaseJavaModule:
            
            
              java
              
              
            
          
          // RNBridgeModule.java
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
public class RNBridgeModule extends ReactContextBaseJavaModule {
 private final ReactApplicationContext reactContext;
public RNBridgeModule(ReactApplicationContext reactContext) {
    super(reactContext);
    this.reactContext = reactContext;
}
// 模块名称(RN 中通过此名称调用)
@Override
public String getName() {
    return "RNBridgeModule";
}
// 暴露获取设备信息的方法给 RN
@ReactMethod
public void getDeviceInfo(Callback callback) {
    String model = android.os.Build.MODEL;
    String version = android.os.Build.VERSION.RELEASE;
    callback.invoke(null, new HashMap<String, String>() {{
        put("model", model);
        put("systemVersion", version);
    }});
}
}- 注册模块 :创建 CustomReactPackage实现ReactPackage:
            
            
              java
              
              
            
          
          // CustomReactPackage.java
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomReactPackage implements ReactPackage {
 @Override
 public List createNativeModules(ReactApplicationContext reactContext) {
        List modules = new ArrayList<>();
        // 注册自定义模块
        modules.add(new RNBridgeModule(reactContext));
        return modules;
    }
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
}
}五、洞窝热更新功能设计
5.1 热更新原理
RN 页面的核心逻辑通过 JS 代码编写,最终打包为 index.bundle(JSBundle),配合资源文件(图片、字体等)运行。热更新本质是通过远程下发新的 JSBundle 和资源,替代本地旧版本,实现无需应用商店审核的更新。
5.2 系统架构
热更新系统分为 客户端 、服务端 、管理后台 三部分:

5.3 客户端实现(核心)
5.3.1 更新包结构
每个热更新包为 zip 压缩文件,包含:

manifest.json元数据示例

5.3.2 核心流程
- 
版本检查 - 
时机:App 启动时、RN 页面首次加载前 
- 
逻辑: 
 
- 
- 
更新包下载与校验 - 
下载:使用原生网络库(支持断点续传、进度监听) 
- 
校验:下载完成后,计算文件 SHA256 与 manifest.json中的hash比对,不一致则删除重试。
 
- 
- 
本地更新与存储 - 
存储路径: - 
iOS: NSDocumentDirectory/RNUpdates/{version}/
- 
Android: getFilesDir()/rn_updates/{version}/
 
- 
- 
版本记录:用 SharedPreferences(Android)或UserDefaults(iOS)记录当前生效版本。
 
- 
- 
生效策略 - 
立即生效:下载完成后调用 reload()重新加载 JSBundle
- 
下次启动生效:记录更新状态,下次打开 RN 页面时加载新包 
 
- 
- 
回滚机制 - 
监听 RN 加载异常(如 onJSException)
- 
发生异常时,切换至上次正常版本,并上报错误日志 
- 
本地保留最近 2 个版本,超出则删除最旧版本 
 
- 
5.3.3 JSBundle 加载优先级
客户端加载 JSBundle 时按以下顺序优先选择:
- 
热更新目录中的最新有效包( 沙盒/RNUpdates/{version}/index.bundle)
- 
应用内置的基础包( assets/index.bundle)
5.4 服务端与管理后台
5.4.1 服务端功能
- 
版本检查接口 GET /api/rn/update/check请求参数:appVersion(App 版本)、rnVersion(当前 RN 版本)、deviceId(设备唯一标识)响应示例:json { "hasUpdate": true, "updateInfo": { "version": "1.0.1", "downloadUrl": "https://xxx.com/updates/update\_v1.0.1.zip", "isForce": false, "description": "修复首页bug" } }
- 
更新包下载接口 GET /api/rn/update/download?version=1.0.1支持断点续传(Range头)、限速控制。
- 
版本管理存储更新包元数据,支持按 App 版本范围、设备比例下发。 
5.4.2 管理后台功能
- 
热更新包上传(自动生成 manifest.json)
- 
配置更新策略(定向发布、灰度比例、强制更新) 
- 
查看更新数据统计(覆盖用户数、成功率、回滚率) 
5.5 安全性保障
- 
传输层:所有接口使用 HTTPS,防止中间人攻击 
- 
内容加密:更新包用 AES-256 加密,客户端内置密钥解密 
- 
接口签名 :客户端请求时携带 timestamp + deviceId + signature(签名算法:SHA256(timestamp + deviceId + appSecret))
- 
权限控制:服务端校验 App 合法性(如包名、签名) 
六、测试与灰度方案
6.1 测试环境
- 
测试服务端:搭建与生产环境一致的测试集群,用于验证更新流程 
- 
测试场景: - 
正常更新(下载→校验→生效) 
- 
边界情况(弱网、断网重连、包损坏、版本不兼容) 
- 
回滚测试(故意上传错误包,验证自动回滚) 
 
- 
6.2 灰度发布
- 
灰度策略: - 
初期 10% 设备(随机选取) 
- 
观察 24 小时,无异常则扩大至 50% 
- 
再观察 24 小时,无异常则全量发布 
 
- 
- 
定向灰度:支持按设备 ID 列表定向下发(用于内部测试) 
七、风险与应对
| 风险点 | 影响范围 | 应对方案 | 
|---|---|---|
| 热更新包与原生版本冲突 | 所有使用该更新的用户 | 服务端严格校验 App 版本范围,不匹配则不下发 | 
| 下载失败 / 超时 | 单用户更新失败 | 实现断点续传 + 3 次重试,非强制更新不阻塞用户 | 
| JS 代码崩溃 | 单用户 RN 页面不可用 | 客户端捕获异常,自动回滚至旧版本,并上报日志 | 
| 原生方法变更导致 RN 调用失败 | 所有使用该更新的用户 | 原生模块接口保持向后兼容,新增方法而非删除旧方法 | 
| 更新包过大导致下载慢 | 低网速用户体验差 | 实现差量更新(仅下发变更文件),压缩资源文件 |