目录
一、背景
1.1 升级动机
React Native 0.70.0 发布于 2022 年 9 月,已进入维护末期。0.72.5 作为 0.72 系列的稳定版本,带来:
- Hermes 引擎 成为默认 JS 引擎,启动速度与内存占用显著优化
- 新架构(Fabric / TurboModules) 基础能力完善,为后续迁移铺垫
- Gradle 7.x + AGP 7.x 原生支持,移除旧版构建兼容代码
- 关键 Bug 修复 与社区生态适配
1.2 升级原则
| 原则 | 说明 |
|---|---|
| 最小化变更 | 仅升级 RN 核心及其紧耦合依赖,不动业务代码 |
| 保持构建工具版本 | Gradle 7.5.1 / AGP 7.4.2 / Kotlin 1.8.20 不变 |
| 前后兼容 | Android 原生侧 API 平滑过渡,JS 侧业务逻辑不改动 |
| 稳定优先 | 优先确保编译通过 + 运行时无闪退,再考虑性能优化 |
二、版本变更一览
2.1 直接变更
| 组件 | 升级前 | 升级后 | 变更原因 |
|---|---|---|---|
| react-native | 0.70.0 | 0.72.5 | 主版本升级 |
| react | 18.1.0 | 18.2.0 | RN 0.72.5 最低要求 React 18.2 |
| compileSdk | 31 | 33 | RN 0.72 要求 |
| targetSdk | 31 | 33 | 跟随 compileSdk |
| buildTools | 31.0.0 | 33.0.0 | 跟随 compileSdk |
| async-storage | 1.16.3 | 1.21.0 | 修复 RN 0.72 peer dependency 不兼容 |
2.2 保持不变
| 组件 | 版本 | 原因 |
|---|---|---|
| Gradle | 7.5.1 | AGP 7.4.2 支持,无需升级 |
| AGP | 7.4.2 | RN 0.72 要求 ≥ 7.3.1,当前已满足 |
| Kotlin | 1.8.20 | 稳定版本,无兼容性问题 |
| Java | 11 | 项目标准,保持不变 |
| minSdk | 23 | 不变 |
2.3 关键架构变化
RN 0.70.0 RN 0.72.5
───────────────────────── ─────────────────────────
JS 引擎:JSC (JavaScriptCore) JS 引擎:Hermes(默认)
Android 产物:react-native AAR Android 产物:react-android + hermes-android
Autolink:native_modules.gradle Autolink:com.facebook.react 插件 + native_modules.gradle
Maven 来源:node_modules 本地 Maven 来源:Maven Central 远程
MainReactPackage:存在 MainReactPackage:保留(AAR 中仍存在)
三、详细修改清单
3.1 构建配置(5 个文件)
package.json
{
"dependencies": {
- "react": "18.1.0",
+ "react": "18.2.0",
- "react-native": "0.70.0",
+ "react-native": "0.72.5",
- "@react-native-async-storage/async-storage": "1.16.3",
+ "@react-native-async-storage/async-storage": "1.21.0"
},
"overrides": {
- "react-native": "0.70.0"
+ "react-native": "0.72.5"
}
}
执行
yarn install安装依赖。
gradle/libs.versions.toml
[versions]
- compileSdk = "31"
+ compileSdk = "33"
- targetSdk = "31"
+ targetSdk = "33"
- buildTools = "31.0.0"
+ buildTools = "33.0.0"
- react-native = "0.70.0"
+ react-native = "0.72.5"
[libraries]
# 核心变更:artifact 从 react-native 拆分为 react-android + hermes-android
- react-native = { module = "com.facebook.react:react-native", version.ref = "react-native" }
+ react-android = { module = "com.facebook.react:react-android", version.ref = "react-native" }
+ hermes-android = { module = "com.facebook.react:hermes-android", version.ref = "react-native" }
app/build.gradle.kts
依赖引用替换:
/** ReactNative相关依赖内容 **/
- implementation(libs.react.native)
- implementation(libs.android.jsc) // JSC 已由 Hermes 替代
+ implementation(libs.react.android)
+ implementation(libs.hermes.android)
NDK 版本和 packaging 配置(Hermes 适配):
android {
+ ndkVersion = "23.1.7779620" // Hermes 预编译库要求的 NDK 版本
packagingOptions {
+ jniLibs {
+ useLegacyPackaging = true // 兼容 AGP 7.x 原生库打包
+ }
pickFirsts.addAll(listOf(
- "lib/arm64-v8a/libc++_shared.so",
- "lib/armeabi-v7a/libc++_shared.so",
- "lib/x86/libc++_shared.so",
- "lib/x86_64/libc++_shared.so"
+ "**/libc++_shared.so", // 通配符覆盖所有架构
+ "**/libjsc.so", // Hermes 相关 so 冲突处理
+ "**/libfbjni.so",
+ "**/libreactnativejni.so",
+ "**/libfolly_runtime.so",
+ "**/libglog.so",
+ "**/libhermes.so"
))
}
}
settings.gradle.kts
dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) // 允许 RN 插件添加仓库
repositories {
- // RN 0.70 本地 maven 仓库(0.72 AAR 已不在本地,从 Maven Central 远程获取)
- maven { url = uri("${rootProject.projectDir}/node_modules/react-native/android") }
}
}
setup_rn_release.sh
- RN_VERSION_GRADLE=$(grep -oP "com.facebook.react:react-native:\K[0-9.]+" ...)
+ RN_VERSION_GRADLE=$(grep -oP "com.facebook.react:react-android:\K[0-9.]+" ...)
3.2 Java / Kotlin 源码(6 个文件)
ModelApplication.java — RN 初始化适配
- import com.facebook.react.shell.MainReactPackage; // RN 0.71+ 类已废弃
- import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
- import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
- import com.th3rdwave.safeareacontext.SafeAreaContextPackage;
- import com.BV.LinearGradient.LinearGradientPackage;
- import com.swmansion.rnscreens.RNScreensPackage;
+ import com.facebook.react.PackageList; // 统一管理社区包注册
@Override
protected List<ReactPackage> getPackages() {
- List<ReactPackage> packages = new ArrayList<>();
- packages.add(new MainReactPackage());
- packages.add(new CustomActionPackage());
- packages.add(new RNGestureHandlerPackage());
- packages.add(new AsyncStoragePackage());
- packages.add(new SafeAreaContextPackage());
- packages.add(new LinearGradientPackage());
- packages.add(new RNScreensPackage());
- return packages;
+ return new PackageList(this).getPackages(); // + CustomActionPackage 在 PackageList 内部
}
ExamPKMateActivity.kt / SmallVideoPlayerActivity.kt — SDK 33 API 变更
- override fun onAnimationEnd(animation: Animator?) // SDK 33 签名从可空改为非空
+ override fun onAnimationEnd(animation: Animator)
- override fun onAnimationStart(animation: Animator?)
+ override fun onAnimationStart(animation: Animator)
InforDetailActivity.java / ChatWebActivity.java — 废弃 API 移除
- webSettings.setAppCacheEnabled(false); // SDK 33 已删除 AppCache API
MainHomeFragmentRN.kt / MomentsFragment.java / MyNewFragmentRN.kt — ReactFragment 构建器适配
ReactFragment.Builder()
.setComponentName("...")
.setLaunchOptions(getLaunchOptions("..."))
+ .setFabricEnabled(false) // RN 0.72 默认 null → Bundle.putBoolean() 时 NPE
.build()
3.3 新增文件(2 个)
app/src/main/java/com/facebook/react/PackageList.java
手工创建的静态 PackageList,等价于 RN CLI autolink 自动生成的内容。
包含MainReactPackage+ 6 个社区包。当新增/删除 RN 社区包时需同步更新。
app/src/main/jniLibs/arm64-v8a/libc++_shared.so
从
react-android:0.72.5AAR 中提取的 C++ STL 运行时(NDK 23 编译)。
解决 Hermes 启动时__emutls_get_address符号缺失导致的闪退。
四、问题与解决方案
本次升级共遇到 8 个问题,按出现顺序记录如下:
| # | 问题 | 阶段 | 根因 | 解决方案 |
|---|---|---|---|---|
| 1 | react-native-gradle-plugin 找不到 | 配置 | classpath 缺版本号 → Maven 解析失败 | 暂时性尝试(见 #4),最终移除插件依赖 |
| 2 | FAIL_ON_PROJECT_REPOS 仓库冲突 | 配置 | RN 0.72 插件动态添加 maven 仓库 | FAIL_ON_PROJECT_REPOS → PREFER_SETTINGS |
| 3 | react {} DSL 编译错误 | 配置 | Kotlin DSL 中 Property 需 .set() 非 = 赋值 | 改为 root.set(file(...)) |
| 4 | PackageList 类找不到 | 编译 | RN Gradle 插件 autolink 与 Kotlin DSL 不兼容 | 核心决策:手工维护静态 PackageList.java |
| 5 | setAppCacheEnabled() 编译错误 | 编译 | SDK 33 删除 WebSettings AppCache API | 移除相关调用(已废弃,无实际作用) |
| 6 | Animator? → Animator 签名不匹配 | 编译 | SDK 33 AnimatorListener 接口 @NonNull 注解变更 | 参数类型改为非空 |
| 7 | ReactFragment.Builder NPE 闪退 | 运行时 | mFabricEnabled 默认 null → Bundle.putBoolean() 拆箱异常 | 3 处调用点添加 .setFabricEnabled(false) |
| 8 | __emutls_get_address 符号缺失 | 运行时 | 旧版 libc++_shared.so 不含 NDK 23 EmuTLS 符号 | 从 react-android AAR 提取正确版本到 jniLibs |
关键问题详解
问题 4:PackageList 无法自动生成
这是整个升级过程中最核心的技术决策点。三个同类项目的对比:
| JianSheMobile | Awesome72(官方 Demo) | accmobile | |
|---|---|---|---|
| RN 版本 | 0.70.0 | 0.72.5 | 0.72.5 |
| Gradle DSL | Groovy .gradle | Groovy .gradle | Kotlin .gradle.kts |
| Autolink 方式 | native_modules.gradle | 插件 + native_modules.gradle | 静态文件 |
ext 函数调用 | 原生支持 | 原生支持 | 跨语言桥接不可行 |
native_modules.gradle(540 行 Groovy)通过 ext 闭包暴露 applyNativeModulesSettingsGradle 和 applyNativeModulesAppBuildGradle 两个扩展函数。Kotlin DSL 无法直接消费 Groovy 闭包,交叉编译边界导致自动生成链路断裂。
决策:手工维护 PackageList.java
- 优势:编译稳定,无 Gradle 插件依赖,版本明确
- 代价:增删社区包时需手动更新(低频操作)
- 参考:官方 Demo 同样使用
MainReactPackage(AAR 中确认存在)
问题 8:libc++_shared.so 符号缺失
错误调用链:
System.loadLibrary("hermes")
→ dlopen("libhermes.so")
→ libhermes.so 依赖 libfolly_runtime.so
→ libfolly_runtime.so 依赖 __emutls_get_address
→ 在旧版 libc++_shared.so 中找不到 → UnsatisfiedLinkError
react-android:0.72.5 AAR 自带 NDK 23 编译的 libc++_shared.so(含 __emutls_get_address),但项目中 ijkplayer、marsxlog 等旧版原生库也提供同名文件。pickFirsts 策略不确定选择哪个版本。
决策:从 AAR 提取正确版本到 jniLibs/
本地 jniLibs 目录的优先级高于 AAR 内嵌文件,确保始终加载正确版本。
五、技术决策:PackageList 方案选择
5.1 方案对比
| 方案 A:RN Gradle 插件 | 方案 B:native_modules.gradle | 方案 C:静态 PackageList(采用) | |
|---|---|---|---|
| Gradle 插件依赖 | 需要 | 不需要 | 不需要 |
| 自动发现新包 | 支持 | 支持 | 需手动维护 |
| Kotlin DSL 兼容 | 部分兼容 | 不兼容 | 完全兼容 |
| 编译稳定性 | 中等 | 低 | 高 |
| 适用场景 | 标准 RN 项目 | Groovy DSL 项目 | 任意结构 |
5.2 采选理由
- 项目特性:accmobile 是 Android 原生项目集成 RN(非
react-native init生成),无标准android/子目录结构。com.facebook.reactGradle 插件的路径推导依赖标准结构。 - 构建语言:Kotlin DSL 与
native_modules.gradle(Groovy)的 ext 机制存在跨语言屏障。 - 维护成本:社区包增删频率低(年均 < 5 次),手动更新成本远低于自动化基础设施建设的投入。
- 长期规划:后续继续评估 RN 新架构(Fabric/TurboModules),届时可重新引入 Gradle 插件。
六、CI/Jenkins 适配指南
6.1 必需环境变更
| 检查项 | 变更前 | 变更后 | 验证命令 |
|---|---|---|---|
| Android SDK Platform | 31 | 33 | sdkmanager --list | grep "platforms;android-33" |
| Android Build Tools | 31.0.0 | 33.0.0 | sdkmanager --list | grep "build-tools;33.0.0" |
| NDK | 未要求 | 23.1.7779620(可自动下载) | AGP 自动处理 |
| Node.js | ≥ 14 | ≥ 14(不变) | node -v |
NDK 自动下载:若 CI 未预装 NDK 23.1,AGP 7.4.2 将自动下载(约 1GB),需确保 CI 有外网访问能力且磁盘 ≥ 5GB 剩余空间。
6.2 已知兼容性项
| 项 | 状态 | 影响 |
|---|---|---|
setup_rn_release.sh 版本检测 | 输出 unknown | 仅警告,不阻塞 |
npm install --legacy-peer-deps | 正常 | RN 0.72 部分 peer dep 不严格 |
PackageList.java | 静态文件 | 已纳入 Git,CI 直接可用 |
libc++_shared.so | 静态文件 | 已纳入 Git,CI 直接可用 |
| Gradle 7.5.1 / AGP 7.4.2 | 不变 | 无需调整 CI 环境 |
6.3 CI 构建建议
# 1. 确保 SDK 33 已安装
yes | $ANDROID_HOME/tools/bin/sdkmanager "platforms;android-33" "build-tools;33.0.0"
# 2. 安装 Node 依赖
npm install --legacy-peer-deps
# 3. 打包 JS bundle
npm run bundle-android
# 4. 执行 Gradle 构建
./gradlew assembleRelease
七、附录
A. 修改文件汇总
修改 (11 files):
├── package.json # RN / React / async-storage 版本升级
├── gradle/libs.versions.toml # SDK / RN 版本 + 依赖 artifact
├── build.gradle.kts # (无变更)
├── settings.gradle.kts # 仓库模式 + 移除本地 maven
├── app/build.gradle.kts # 依赖引用 / NDK / packaging
├── app/src/main/java/.../
│ ├── ModelApplication.java # PackageList 替代手动注册
│ ├── ExamPKMateActivity.kt # Animator 签名适配
│ ├── SmallVideoPlayerActivity.kt # Animator 签名适配
│ ├── InforDetailActivity.java # 移除 setAppCacheEnabled
│ ├── ChatWebActivity.java # 移除 setAppCacheEnabled
│ ├── MainHomeFragmentRN.kt # ReactFragment 构建器适配
│ ├── MomentsFragment.java # ReactFragment 构建器适配
│ └── MyNewFragmentRN.kt # ReactFragment 构建器适配
└── setup_rn_release.sh # 版本检测正则更新
新增 (2 files):
├── app/src/main/java/com/facebook/react/PackageList.java
└── app/src/main/jniLibs/arm64-v8a/libc++_shared.so
B. 关键数据
| 指标 | 数值 |
|---|---|
| 修改文件数 | 13 |
| 新增文件数 | 2 |
| 解决编译/运行时问题数 | 8 |
| 总耗时 | ~4 小时 |
| 业务代码改动 | 0(纯适配性修改) |