React Native和底层的通信原理

254 阅读6分钟

简单回答

  • React Native 通过一个称为“桥”的机制与底层原生代码通信,这是一种双向且异步的通信方式。
  • 通信过程涉及将消息序列化为 JSON 格式,通过不同的线程传递。
  • JavaScript 可以调用原生模块中的方法,原生代码也可以通过事件发送消息给 JavaScript。
  • 桥管理一个消息队列来高效处理这些通信。

经典架构 - The Bridge (桥)

在 React Native 的早期和当前大部分应用中,其核心通信机制是一个叫做  “The Bridge”  的东西。你可以把它想象成一座连接两个独立世界的桥梁。

这两个世界分别是:

  1. JavaScript 世界: 运行你的 React 代码、业务逻辑。它由一个 JS 引擎驱动(在 iOS 上是 JavaScriptCore,在 Android 上过去是 JSC,现在越来越多地使用 Hermes)。
  2. Native 世界: 运行原生代码(iOS 的 Objective-C/Swift,Android 的 Java/Kotlin),负责渲染 UI、调用设备 API(如摄像头、GPS)等。

这两个世界运行在不同的线程上,并且它们不能直接互相调用对方的对象或方法。因此,它们需要一个“翻译官”和“信使”——这就是 Bridge 的角色。

1.1 Bridge 的工作原理

Bridge 的通信是 异步、序列化、批量化 的。

  1. 三个关键线程:
  • 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 去渲染。
  1. 通信流程(JS → Native) :
  • 场景: 你在 JS 代码中写了一个 ,或者点击按钮改变了一个状态,导致一个  的内容需要更新。
  • 步骤:
    1. JS Thread 计算出 UI 变更,例如:“创建一个 View,背景色为红色” 或 “更新 ID 为 5 的 Text 组件,内容为 'Hello'”。
    2. 这个指令被打包成一个 JSON 字符串
    3. 这个 JSON 字符串通过 Bridge 发送到 Native 世界。
    4. Native 世界 接收并解析这个 JSON 字符串。
    5. Shadow Thread 根据指令计算布局。
    6. Native/UI Thread 执行最终的指令,比如 new UIView() 或 textView.setText("Hello"),用户最终在屏幕上看到变化。

3. 通信流程(Native → JS) :

  • 场景: 用户点击了一个原生按钮,或者设备 GPS 位置发生了变化。
  • 步骤:
    1. Native/UI Thread 捕捉到这个事件(如 onClick)。
    2. Native 代码将事件信息(如事件名称 onPress,目标组件 ID)打包成一个 JSON 字符串
    3. 通过 Bridge 将这个 JSON 字符串发送到 JS 世界。
    4. 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++ 对象)的引用,并直接调用其方法。

核心改变:

  1. 告别 JSON 序列化: JS 和 Native 不再需要通过 JSON 字符串来回传递消息。JSI 允许 JS 引擎直接操作 C++ 对象,就像操作普通的 JS 对象一样。这极大地减少了通信开销。
  2. 同步执行成为可能: 因为 JS 可以直接调用 C++ 对象的方法,所以可以实现同步调用。这对需要立即得到结果的场景(如高频手势处理)是革命性的。
  3. 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),可以直接对话,甚至可以拿到对方手里的东西(对象引用)来操作。这非常快,而且实时。

前端大大大

微信公众号:【前端大大大】

qrcode_for_gh_c467a0436534_258.jpg