简单回答
- React Native 通过一个称为“桥”的机制与底层原生代码通信,这是一种双向且异步的通信方式。
- 通信过程涉及将消息序列化为 JSON 格式,通过不同的线程传递。
- JavaScript 可以调用原生模块中的方法,原生代码也可以通过事件发送消息给 JavaScript。
- 桥管理一个消息队列来高效处理这些通信。
经典架构 - The Bridge (桥)
在 React Native 的早期和当前大部分应用中,其核心通信机制是一个叫做 “The Bridge” 的东西。你可以把它想象成一座连接两个独立世界的桥梁。
这两个世界分别是:
- JavaScript 世界: 运行你的 React 代码、业务逻辑。它由一个 JS 引擎驱动(在 iOS 上是 JavaScriptCore,在 Android 上过去是 JSC,现在越来越多地使用 Hermes)。
- Native 世界: 运行原生代码(iOS 的 Objective-C/Swift,Android 的 Java/Kotlin),负责渲染 UI、调用设备 API(如摄像头、GPS)等。
这两个世界运行在不同的线程上,并且它们不能直接互相调用对方的对象或方法。因此,它们需要一个“翻译官”和“信使”——这就是 Bridge 的角色。
1.1 Bridge 的工作原理
Bridge 的通信是 异步、序列化、批量化 的。
- 三个关键线程:
- JS Thread: 你的 JS 代码在这里运行。当一个组件状态改变时,React 会计算出需要对 UI 做出的最小变更(类似 Web 的 Virtual DOM Diff)。
- Native/UI Thread: 这是原生平台的“主线程”。它是唯一可以操作和渲染原生 UI 的线程。为了保证 UI 的流畅,这个线程绝对不能被阻塞。
- Shadow Thread (或 Layout Thread) : 这是一个后台线程。它接收来自 JS 线程的 UI 变更指令,然后使用一个叫做 Yoga 的布局引擎计算出 UI 元素在屏幕上的最终位置和尺寸。计算完成后,再把这些具体的布局信息传递给 Native/UI Thread 去渲染。
- 通信流程(JS → Native) :
- 场景: 你在 JS 代码中写了一个 ,或者点击按钮改变了一个状态,导致一个 的内容需要更新。
- 步骤:
- JS Thread 计算出 UI 变更,例如:“创建一个 View,背景色为红色” 或 “更新 ID 为 5 的 Text 组件,内容为 'Hello'”。
- 这个指令被打包成一个 JSON 字符串。
- 这个 JSON 字符串通过 Bridge 发送到 Native 世界。
- Native 世界 接收并解析这个 JSON 字符串。
- Shadow Thread 根据指令计算布局。
- Native/UI Thread 执行最终的指令,比如 new UIView() 或 textView.setText("Hello"),用户最终在屏幕上看到变化。
3. 通信流程(Native → JS) :
- 场景: 用户点击了一个原生按钮,或者设备 GPS 位置发生了变化。
- 步骤:
- Native/UI Thread 捕捉到这个事件(如 onClick)。
- Native 代码将事件信息(如事件名称 onPress,目标组件 ID)打包成一个 JSON 字符串。
- 通过 Bridge 将这个 JSON 字符串发送到 JS 世界。
- JS Thread 接收并解析 JSON,然后调用你写的 onPress 回调函数。
1.2 Bridge 架构的缺点
- 异步限制: 所有通信都是异步的。这意味着 JS 无法同步地获取原生 UI 的尺寸或状态,这在处理复杂手势或动画时会变得困难。
- 序列化开销: 所有跨界数据都必须序列化和反序列化为 JSON 字符串。对于大量或频繁的数据交换(如列表滚动时的事件),这个过程会非常耗时,成为性能瓶瓶颈。
- 拥堵: 如果 JS 和 Native 之间有大量消息需要传递(例如,在快速滑动列表时),Bridge 可能会被“堵塞”,导致延迟和掉帧。
- 内存开销: 数据在两边都需要被复制和存储,增加了内存消耗。
新一代架构 - JSI (JavaScript Interface)
为了解决Bridge的性能问题,React Native团队推出了一个全新的架构,核心是 JSI (JavaScript Interface) 。
JSI 不是对 Bridge 的优化,而是彻底的替代。
2.1 JSI 的工作原理
JSI 是一个用 C++ 编写的轻量级通用层,它允许 JavaScript 直接持有对原生对象(C++ 对象)的引用,并直接调用其方法。
核心改变:
- 告别 JSON 序列化: JS 和 Native 不再需要通过 JSON 字符串来回传递消息。JSI 允许 JS 引擎直接操作 C++ 对象,就像操作普通的 JS 对象一样。这极大地减少了通信开销。
- 同步执行成为可能: 因为 JS 可以直接调用 C++ 对象的方法,所以可以实现同步调用。这对需要立即得到结果的场景(如高频手势处理)是革命性的。
- JS 引擎解耦: JSI 是一个标准接口,任何实现了这个接口的 JS 引擎都可以与 RN 的 Native 端配合工作。这使得替换 JS 引擎(如从 JSC 切换到性能更好的 Hermes)变得非常容易。
2.2 基于 JSI 的两大支柱:Turbo Modules 和 Fabric
JSI 只是底层接口,为了让开发者能使用它,RN 推出了两个新的概念:
Turbo Modules (新一代原生模块) :
- 懒加载: 不同于旧架构在 App 启动时就加载所有原生模块,Turbo Modules 只在第一次被 JS 代码 require 或 import 时才被加载,显著加快了 App 的启动速度。
- 强类型接口: 通过代码生成,确保 JS 和 Native 之间的方法调用是类型安全的。
- 直接调用: JS 通过 JSI 直接调用原生模块的方法,没有了 Bridge 的延迟。
Fabric (新一代渲染器) :
- 是 RN 的新 UI 层,它完全利用 JSI 的能力。
- 跨线程的 C++ 核心: 以前的 Shadow Thread 现在被一个用 C++ 实现的、可在任何线程中访问的核心所取代。
- 同步 UI 操作: Fabric 允许 JS 在需要时同步地执行高优先级的 UI 更新,这对于动画和手势响应至关重要,能带来更接近原生的流畅体验。
- React 18 并发特性: Fabric 的架构设计可以更好地支持 React 18 的并发渲染等新特性。
总结与对比
| 特性 | 经典 Bridge 架构 | 新一代 JSI 架构 |
|---|---|---|
| 通信方式 | 异步消息队列 | 直接方法调用(同步/异步均可) |
| 数据格式 | JSON 字符串 | 直接传递 C++ 对象引用 |
| 性能 | 存在瓶颈,高频通信时易延迟 | 高性能,开销极小 |
| 调用时机 | 只能异步 | 支持同步调用 |
| 原生模块 | 启动时全部加载 | 按需加载 (Turbo Modules) |
| UI 渲染 | 异步渲染 (Legacy Renderer) | 支持同步高优更新 (Fabric) |
| 核心 | The Bridge (桥) | JSI (C++ 接口) |
简单来说:
- 旧架构 就像两个隔着房间喊话的人,他们需要一个翻译官(Bridge)把每句话写在纸条上(JSON),然后传递过去。这很慢,而且有延迟。
- 新架构 则是给了 JS 一个直通 Native 的电话(JSI),可以直接对话,甚至可以拿到对方手里的东西(对象引用)来操作。这非常快,而且实时。
前端大大大
微信公众号:【前端大大大】