Bridge 是 React Native 的一个架构组件,负责 JavaScript 线程与 iOS 和 Android 原生环境之间的异步通信。它通过队列传输序列化的 JSON 消息,允许从 JS 代码调用原生 API。根据 Meta,2024 的数据,Bridge 仍然是现有应用程序的基础,尽管在性能上不如基于 JSI 的新架构。
要点
Bridge(桥梁) — 是 React Native 的一个关键架构元素,负责在执行业务逻辑的 JavaScript 线程与 iOS 和 Android 的原生线程之间提供双向异步通信。自 React Native 于 2015 年发布以来,Bridge 一直是 JS 代码与平台 API(摄像头、地理定位、文件系统、通知和其他原生功能)交互的唯一方式。
Bridge 架构基于消息队列(message queue)原理。当 JavaScript 代码调用原生方法时,请求被序列化为 JSON 字符串,放入队列,并异步发送到原生端。原生代码处理请求,执行相应操作,并通过同一队列将结果返回给 JS 线程。根据 Meta 在 React Conf 2021 上的报告,在普通应用程序中,每秒通过 Bridge 传输多达 10,000 条消息。
参与 Bridge 工作的主要线程:JavaScript Thread(执行 JS 代码)、Native Thread(执行原生操作)和 Shadow Thread(使用 Yoga 计算布局)。每个线程独立工作,从而确保界面的响应性——原生动画不会被 JS 计算阻塞。
Bridge 使用三个关键机制进行通信:MessageQueue、JSON 序列化和消息批处理。MessageQueue 是 React Native 的内部组件,管理 JS 与原生端之间的调用队列。每次调用原生方法时,它被放入队列,序列化,并以批处理方式发送以优化性能。
MessageQueue 基于批处理原则工作:原生方法调用被累积起来,每 5-15 毫秒作为一组(batch)发送。这减少了序列化开销,因为多个调用被打包到一个 JSON 包中。在原生端,消息被反序列化并分发到相应的模块。
原生模块通过宏或注解自动注册。在 iOS 上使用 RCT_EXPORT_MODULE 宏,在 Android 上使用 @ReactMethod 注解。React Native 在应用程序启动时扫描已注册的模块,并构建所有可用方法的配置 JSON 映射。该映射被传输到 JS 环境,JavaScript 从而知道哪些方法可以调用。
数据经过以下路径:JavaScript 调用 NativeModules.CalendarModule.createCalendarEvent()。该方法被序列化为带有模块标识符、方法名称和参数的 JSON 消息。消息进入 MessageQueue 队列。在原生线程中,消息被反序列化并传递到相应的模块。执行结果被序列化回来,并以 Promise 或回调的形式发送到 JS 线程。
// 通过Bridge从JavaScript调用原生模块
import { NativeModules } from 'react-native';
const CalendarModule = NativeModules.CalendarModule;
CalendarModule.createCalendarEvent('Test Event', 'Office')
.then(eventId => {
console.log('已创建事件,id:', eventId);
})
.catch(error => {
console.error('失败:', error);
});
在 iOS 原生端,模块看起来像一个带有 RCT_EXPORT_MODULE 宏的 Objective-C 类。方法通过 RCT_EXPORT_METHOD 宏导出,React Native 自动将其注册到 Bridge 中。参数按位置传递,必须与支持的 JSON 类型匹配:NSString、NSNumber、NSArray、NSDictionary、BOOL。
// Bridge中的iOS原生模块注册
@interface CalendarModule () RCT_EXPORT_MODULE()
@end
@implementation CalendarModule
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSNumber *eventId = createEvent(name, location);
resolve(eventId);
}
@end
Bridge 具有一系列根本性的性能限制。其中最主要的是强制异步性和序列化。每次调用原生方法都将数据转换为 JSON 字符串,这会增加延迟并消耗内存。对于大量数据的操作,例如图像处理或视频处理,这成为瓶颈。
JSON 序列化和反序列化会占用处理器时间和内存。每条消息必须在 JS 端转换为字符串,通过桥传输,并在原生端解析。根据 Callstack(2022)的测试,通过 Bridge 序列化一个包含 10,000 个数字的数组大约需要 30-50 毫秒,这对于高频调用来说是不可接受的。
Bridge 未针对传输大型二进制数据进行优化。照片、音频文件和视频流需要替代方法——例如,将文件写入磁盘并将路径作为字符串传输。这为文件系统的读写带来了额外的开销。
认识到这些限制后,Meta 团队开发了 React Native 的新架构,其中 Bridge 被 JSI(JavaScript Interface)和 Turbo Module 取代。JSI 允许直接调用原生方法而无需序列化,从而消除了 Bridge 的主要缺点。
Bridge 和 Turbo Module 的对比显示出架构方法上的根本差异。Bridge 使用带有 JSON 序列化的异步消息队列,而 Turbo Module 通过 JSI(JavaScript 和 C++ 之间的直接接口)工作,允许同步调用原生方法而无需数据转换。
| 特性 | Bridge | Turbo Module |
|---|---|---|
| 调用类型 | 异步 | 同步和异步 |
| 序列化 | 每次调用都使用 JSON | JSI 对象,无需复制 |
| 性能 | 中等 | 高 |
| 类型系统 | 动态 | 静态(Codegen) |
| 加载 | 启动时加载所有模块 | 懒加载(按需) |
Bridge 和 Turbo Module 之间的选择取决于 React Native 的版本。对于 React Native 0.72 及更早版本的项目,Bridge 仍然是主要机制。从 React Native 0.73 开始,Metro 和新架构得到并行支持,允许逐步迁移。完全过渡到 Turbo Module 需要升级到 React Native 0.76+ 并在配置中启用新架构。
让我们以日历模块为例,看看通过 Bridge 创建和使用 Native Module 的完整周期。该模块将创建事件并返回其标识符。此示例涵盖了 iOS 和 Android 两个平台的配置。
在 Android 上,Native Module 创建为继承自 ReactContextBaseJavaModule 的 Java 类。@ReactMethod 注解将方法导出到 Bridge。对于 Promise,使用 com.facebook.react.bridge 中的 Promise 接口。
public class CalendarModule extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "CalendarModule";
}
@ReactMethod
public void createCalendarEvent(
String name,
String location,
Promise promise) {
try {
Integer eventId = createCalendarEventNative(name, location);
promise.resolve(eventId);
} catch (Exception e) {
promise.reject("EVENT_ERROR", e.getMessage());
}
}
}
模块通过 @ReactModule 或手动在应用程序包中注册。React Native 自动检测并将其添加到 Bridge。注册后,模块可通过 NativeModules 从 JavaScript 访问。
public class CalendarPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.asList(
new CalendarModule(reactContext)
);
}
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
需要注意的是,Bridge 在添加新模块时需要重新启动应用程序,因为配置映射在初始化时只构建一次。这将其与Turbo Module 区分开来,后者是懒加载的,并支持无需重启的模块热重载。
常见问题
Bridge 始终使用异步队列和 JSON 序列化,而通过 JSI 的直接传输是同步的且无需复制数据。Bridge 因序列化产生延迟,但确保了线程的隔离性。
不可以,Bridge 仅支持异步调用。同步交互需要采用 JSI 和 Turbo Module 的新架构。这是 React Native 0.76+ 中已解决的关键限制之一。
Bridge 支持可序列化为 JSON 的类型:字符串、数字、布尔值、数组、字典(对象)。二进制数据,如图像,必须通过文件系统或 base64 编码传输。
使用 React DevTools 和 React Native 分析器进行测量。Performance 选项卡显示 Bridge 队列中的消息数量和延迟。还提供了 react-native-bridge-spy 包用于流量监控。
对于需要高性能的项目或在 React Native 0.76+ 上创建新应用程序时,建议迁移。对于现有项目,迁移可以是逐步的——两种架构并行工作。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。