React Native JSI 深入剖析 — 第 7 部分中文技术整理:把 C++ 能力接到 iOS 和 Android

文章主旨

前面几篇已经回答了:

  • JSI 为什么快;
  • host function 和 HostObject 怎么写;
  • 内存所有权怎么理解。

但这些代码都还停留在"概念上可运行"。Part 7 解决的是更现实的问题:

你的 C++ 代码怎么真的接进 React Native 的 iOS 和 Android 工程里?

原文把它叫作 plumbing,很贴切。因为这一篇主要不是算法,也不是架构抽象,而是"平台装配"。

这篇要解决的不是 C++,而是两套平台语言边界

一个很关键的判断是:

  • 你的业务核心可以写成跨平台共享 C++;
  • 但 iOS 和 Android 并不会原生"直接理解"这段 C++;
  • 你必须分别做一层平台侧 wiring。

所以真正的结构是:

  • 中间是一套共享 C++ engine;
  • 左边用 Objective-C++ 接 iOS;
  • 右边用 JNI + CMake 接 Android。

这也是原文"一个发动机,两条装配线"的类比含义。

一个标准 JSI 模块的目录结构应该怎么想

原文先给出了典型文件布局,这其实非常有参考价值。一个生产级 JSI 模块通常会有三层:

1. 共享 C++ 层

放:

  • install() 入口;
  • host function / HostObject 实现;
  • 共享数据结构;
  • 通用算法与内存管理逻辑。

2. iOS wiring 层

放:

  • .mm Objective-C++ 文件;
  • 与 RN iOS runtime 对接的安装逻辑;
  • podspec / CocoaPods 配置。

3. Android wiring 层

放:

  • Kotlin / Java 的 module 壳;
  • JNI glue code;
  • CMakeLists.txt

这个结构很重要,因为它说明一件事:

真正应尽量跨平台共享的是 C++ 核心,而不是整套平台集成代码。

为什么 iOS 要用 Objective-C++

原文在 iOS 部分讲得很清楚:问题不只是"语言喜好",而是调用链兼容性。

iOS 侧的现实是:

  • React Native 的 iOS 内部层大量是 Objective-C / Objective-C++;
  • 你的 JSI 实现是 C++;
  • 普通 Objective-C 不会直接调用 C++;
  • Swift 虽然近年有 C++ interop,但在 RN 生态里还不够成熟和主流。

所以最务实的桥梁语言就是 .mm

Objective-C++ 可以在同一个文件里同时理解 Objective-C 和 C++。

这也是为什么很多 RN JSI 库的 iOS 入口文件都是 .mm 而不是 .m.swift

iOS 侧真正做了什么

iOS wiring 的任务可以压缩成一句话:

在正确的时机拿到 jsi::Runtime,然后调用你的 install(runtime)

围绕这句话,iOS 侧通常要做的事包括:

  • 在原生模块初始化时接入 RN bridge;
  • 获取底层 runtime;
  • 进入 Objective-C++ 层;
  • 调你的共享 C++ 安装函数;
  • 把能力注入到 JS runtime。

Podspec 的作用则不是"注册 JSI 功能本身",而是确保:

  • 这些源文件被编译;
  • C++ 标准配置正确;
  • 头文件路径和依赖关系正确。

所以 podspec 更像 build plumbing,而不是逻辑 plumbing。

Android 比 iOS 更分层:Kotlin、JNI、CMake 三层配合

Android 部分是整篇最值得整理的地方。原文把它拆成三层,我觉得这个拆法非常清楚。

第 1 层:Kotlin / Java 模块声明

这一层负责:

  • 让 React Native 知道有这个模块;
  • 在合适时机暴露原生入口;
  • 把 runtime 指针或上下文继续传下去。

它是 RN 世界和 Android App 世界的连接点。

第 2 层:JNI glue

这一层负责:

  • 让 JVM 世界和 C++ 世界对接;
  • 把 Java/Kotlin 调用映射到 C++ 函数;
  • 处理 jlong 等桥接参数;
  • 最终触达 install()

这一步的本质不是"业务逻辑",而是 ABI 级别的语言边界适配。

第 3 层:CMake

这一层负责:

  • 告诉 Android NDK 如何编译你的 C++;
  • 链接 RN / JSI 相关依赖;
  • 产出 .so

所以 Android wiring 的完整心智模型不是"写一个 native module 就完了",而是:

Kotlin 宣告入口,JNI 负责语言桥接,CMake 负责把 C++ 真正编出来。

为什么 Android 经常把 runtime 当作 jlong

原文 FAQ 里提到一个很常见的疑问:为什么不是传"一个 runtime 对象",而是把 runtime 指针当 long / jlong 传递。

本质原因很简单:

  • JVM 不理解 C++ 对象模型;
  • JNI 边界上最现实的通道通常是原始地址值;
  • 所以你传的不是"高层语义对象",而是"底层指针句柄"。

这虽然不优雅,但很常见,也符合 JNI 这类边界的真实工作方式。

平台 wiring 的关键不是"怎么接",而是"什么时候接"

原文专门讲了 initialization timeline,这一点很重要。

JSI 安装不是随便找个模块构造时机就行,而是必须满足:

  • runtime 已经存在;
  • bridge / engine 已经初始化;
  • 你注入的函数或对象不会错过 JS 侧使用时机;
  • 也不会早到访问空 runtime。

所以 platform wiring 里最容易出的问题不是代码写不出来,而是:

  • 装得太早;
  • 装得太晚;
  • runtime 生命周期判断错误;
  • 热重载、重建 bridge 后没有重新安装。

这也是为什么"能编过"和"能稳定运行"之间还有一大段距离。

手工 wiring 的主要代价

原文没有回避这个方案的成本,主要有三类。

1. 依赖内部实现细节

无论 iOS 还是 Android,很多 runtime 获取路径都不是特别稳定、公开、长期承诺不变的高层 API。它们能用,但会受 RN 内部演进影响。

2. 双平台构建维护成本

你不只是维护一份 C++ 代码,还要维护:

  • Podspec
  • Objective-C++
  • Kotlin / Java 壳
  • JNI glue
  • CMake

3. 调试链路变长

出问题时,可能卡在:

  • JS 没调用到;
  • 调到了原生模块但没进 JNI;
  • JNI 进了但 .so 没正确导出;
  • iOS / Android 某一侧运行时没正确初始化。

这意味着平台 wiring 不是"写一次就忘",而是生产维护成本的一部分。

我的补充理解

1. Part 7 本质上是在教你把"JSI demo"变成"可交付模块"

前几篇你学到的是机制;这一篇你才真正开始进入"库作者"视角。因为到了这里,你面对的已不是 createFromHostFunction 本身,而是:

  • 包结构怎么分;
  • 双平台怎么编;
  • runtime 何时拿;
  • 安装时机如何稳定。

2. 共享 C++ 是价值,平台 wiring 是成本

这是所有 JSI 模块都绕不开的基本面。你之所以愿意承担 iOS / Android 各自的一层集成成本,是因为:

  • 核心算法只写一份;
  • 生命周期和性能策略也能共享;
  • 高性能能力不必双写。

如果共享层很薄,这套成本未必划算;如果共享层很厚,这套成本就很值得。

3. 手工 wiring 也解释了为什么后面会讨论 TurboModules

Part 11 会回头比较 TurboModules、pure JSI、pure C++。Part 7 提前暴露了 pure JSI 的一个现实代价:

你拥有最大控制权,同时也承担最多平台装配细节。

关键结论

  • JSI 模块的核心逻辑可以写成共享 C++,但 iOS 和 Android 都需要各自平台 wiring。
  • iOS 的现实桥梁语言是 Objective-C++,Android 的现实路径是 Kotlin/Java + JNI + CMake。
  • 平台集成的目标都是一样的:在正确时机拿到 jsi::Runtime,然后调用共享的 install()
  • build 配置、初始化时机、runtime 生命周期判断,是平台 wiring 里最常见的稳定性来源。
  • pure JSI 的高控制权,直接对应更高的平台维护成本。

下一步适合看什么

紧接着看的应该是 Part 8,因为到这里虽然"能接进来了",但所有调用仍然是同步阻塞 JS 线程的。

下一篇真正回答的是:

重活怎么放到后台线程,还能安全地把结果送回 JS?

相关推荐
jt君424264 小时前
React Native JSI 深入剖析 — 第 6 部分中文技术整理:跨 JS 与 C++ 两个世界的内存所有权
react native
jt君424261 天前
React Native JSI 深入剖析 — 第 5 部分中文技术整理:用 HostObject 把 C++ 类暴露给 JavaScript
前端·react native
花椒技术5 天前
RN 多包热更新实践:更新校验、运行时加载与 Bridge 缓存治理
react native·react.js·harmonyos
互联网推荐官5 天前
上海 APP 开发服务甄选:技术架构设计、全维度判断框架
javascript·react native·react.js·app开发·开发经验·上海
墨狂之逸才9 天前
TRAE IDE 提效实战指南:少加班,多摸鱼
react native
墨狂之逸才9 天前
给 AI Coding Agent 装上 React Native 外挂:callstackincubator/agent-skills 上手指南
react native
墨狂之逸才9 天前
# React Native 人脸识别 UI 方案全对比:嵌入组件 · Activity · Dialog
react native
沙漠10 天前
ReactNative总结系列四 --- FlatList白屏卡顿优化
react native·性能优化
wordbaby12 天前
rn-cross-calendar:一个兼容 React 18/19、RN/RNOH 的跨平台日历组件
前端·react native·harmonyos