前言
虽然在跨平台这块谷歌搞了 Flutter 出来,但是从目前的生态和大厂应用上来讲,ReactNative 优势更明显些。虽然这是一个 15 年就推出的跨平台框架,但是这几年 ReactNative 团队也一直在对它进行优化,尤其在大家平时诟病的 Bridge 那块,做了很大的调整,代码也基本上从开始的 OC 形式直接改成了 cpp 的桥接,基本上达到了和 JavaScript 的无缝衔接,另外一点这样也可以做到安卓 iOS 双端代码统一。个人觉得学习它的设计思想和设计模式要远远大于它未来的生态,所以基于 0.61.0 版本,总结了一下 ReactNative 的源码,本篇作为开篇先从 ReactNative 的业务代码开始,一步步纵深探究下其内部原理。
从ReactNative的初始化开始
根据 ReactNative 官方文档描述,当我们想要一个 ViewController 成为 RN 的容器的话,具体实现应该是这样的:
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
// 初始化rootView
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName: @"RNHighScores"
initialProperties:nil
launchOptions: nil];
self.view = rootView;
}
复制代码
- 根据本地 JSBundle 的 url 初始化一个 RootView。
- 再把这个 rootView 赋值给 VC 的 View。
在 RCTRootView 初始化方法里面还会创建一个 RCTBridge,这个 RCTBridge 就是 ReactNative 框架层中的 Bridge 部分,也是极其重要的部分
// RCTRootView.m
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL
moduleProvider:nil
launchOptions:launchOptions];
return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}
复制代码
// RCTBridge.m
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleProvider:(RCTBridgeModuleListProvider)block
launchOptions:(NSDictionary *)launchOptions
{
return [self initWithDelegate:nil
bundleURL:bundleURL
moduleProvider:block
launchOptions:launchOptions];
}
- (void)setUp
{
...
Class bridgeClass = self.bridgeClass;
...
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
[self.batchedBridge start];
}
- (Class)bridgeClass
{
return [RCTCxxBridge class];
}
复制代码
在 RCTRootView.m
内部最后会走到 RCTCxxBridge 的 -start
方法。
// RCTCxxBridge.mm
- (void)start
{
// 1.创建一个常驻的线程
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
[_jsThread start];
dispatch_group_t prepareBridge = dispatch_group_create();
// 2.加载原生模块
[self registerExtraModules];
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules];
// 3.创建Instance实例
_reactInstance.reset(new Instance);
__weak RCTCxxBridge *weakSelf = self;
// 4.JSExecutorFactory工厂,生产环境它的真实类型是JSIExecutor
// Prepare executor factory (shared_ptr for copy into block)
std::shared_ptr<JSExecutorFactory> executorFactory;
if (!self.executorClass) {
if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>) self.delegate;
executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
}
if (!executorFactory) {
executorFactory = std::make_shared<JSCExecutorFactory>(nullptr);
}
} else {
id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
}));
}
// 5.初始化底层Bridge
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 6.加载JS
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
} onProgress:^(RCTLoadingProgress *progressData) {
...
}];
// 7.执行JS
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
复制代码
-start
方法贯穿了整个 ReactNative 的启动流程,整个 -start
方法主要做了以下事情:
- 创建了个 JS 线程 _jsThread,并在内部开了个 runloop 让它长驻,初始化底层 Bridge、执行 JS 代码都是在这个线程里执行。
- 加载原生模块,将所有暴漏给JS使用的原生 RCTBridgeModule 初始化,每个原生模块都被初始化为 RCTModuleData,并将其仍到数组里。
- 初始化 Instance 实例,Instance 是公共层 Bridge 的最上层,数据公共部分的入口,到这里已经进入了 cpp 文件。Instance 是对底层 Bridge 的一层包装,提供了一些执行Call JS 的API。
- JSExecutorFactory 使用工厂模式,在不同场景构建出不同的 JSExecutor,其中生产环境使用 JSCExecutorFactory,用于构建 JSIExecutor,这个东西是底层 Bridge 的一个中间环节,先按住不讲后面细说。
- 在 JS 线程初始化底层 Bridge。
- 加载 JS 代码。
- 执行 JS 代码。
上面源码用到了一个 dispatch_group_t
类型的 prepareBridge
,dispatch_group_t
和 dispatch_group_notify
联合使用保证异步代码同步按顺序执行,从上面的启动流程来看,原生模块的加载在主线程,底层 Bridge的初始化是在 JS 线程,JS 代码的加载可以同步也可以异步,这些工作都是异步执行的,dispatch_group_notify
能够保证这些工作都执行完毕,在执行 JS 代码。关于 Bridge 的构建和 JS 代码的加载执行,流程都较长下面会细说。
原生模块加载
先用一个官方文档的例子,看看原生的模块都是如何被 ReactNative 进行加载并提供给 JS 侧使用的,在 ReactNative 中创建一个原生模块,以文档日历模块为例:
// CalendarManager.h
#import <React/RCTBridgeModule.h>
@interface CalendarManager : NSObject <RCTBridgeModule>
@end
复制代码
// CalendarManager.m
#import "CalendarManager.h"
#import <React/RCTLog.h>
@implementation CalendarManager
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
@end
复制代码
- 创建一个 NSObject 的子类,叫 CalendarManager,并且遵循
RCTBridgeModule
协议。 - 在源文件内实现
RCT_EXPORT_MODULE()
。 - 使用
RCT_EXPORT_METHOD
宏,实现需要给 JS 导出的方法。
这样一个原生模块就创建完了,可以让 JS 侧直接调用:
// index.js
import {NativeModules} from 'react-native';
const CalendarManager = NativeModules.CalendarManager;
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');
复制代码
这样一个简单的调用就结束了,我们只知道这样调用就能把参数传递到 CalendarManager里面来,然而并不知道 ReactNative背后做了什么,知其然不知其所以然 那我们深入看下源码,先看看 RCT_EXPORT_MODULE()
做了什么。
这是一个宏的嵌套函数,层层展开如下:
// RCTBridgeModule.h
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }
void RCTRegisterModule(Class moduleClass)
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTModuleClasses = [NSMutableArray new];
RCTModuleClassesSyncQueue = dispatch_queue_create("com.facebook.react.ModuleClassesSyncQueue", DISPATCH_QUEUE_CONCURRENT);
});
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
@"%@ does not conform to the RCTBridgeModule protocol",
moduleClass);
// Register module
dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
[RCTModuleClasses addObject:moduleClass];
});
}
复制代码
展开宏自动帮我们实现了三个函数,@#
的意思是自动把宏的参数 js_name
转成字符通过 +moduleName
返回原生模块名称,重写 + (void)load
函数,调用 RCTRegisterModule()
把类注册到原生模块类集合。App在启动后,实际上所有模块都走了 +load
方法,也就是说都调用了 RCTRegisterModule()
方法进行注册。RCTRegisterModule()
里面首先判断注册的模块是否遵循了 RCTBridgeModule
协议,如果遵循了则会放入到 RCTModuleClasses
数组中,这里处于数组线程安全考虑,使用栅栏函数加了读写锁,其实还有个读函数后面会说。
第一个宏说完了再来看看第二个宏,RCT_EXPORT_METHOD
都做了些啥。RCT_EXPORT_METHOD
也是个展开宏,但是比上面的要复杂些,展开如下:
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
//展开如下
+ (const RCTMethodInfo *)__rct_export__390 {
static RCTMethodInfo config = {
"",
"addEvent:(NSString *)name location:(NSString *)location",
NO,
};
return &config;
}
- (void)addEvent:(NSString *)name location:(NSString *)location
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
复制代码
看展开结果,RCT_EXPORT_METHOD
宏首先帮我们补全了 - (void)
,使得 - (void)
加上参数还原了我们上面给 JS 定义的方法,还声成了一个以 __rct_export__
拼接上 __LINE__
与 __COUNTER__
为方法名的函数,这是两个C语言宏,分别代表着行号与一个内置计数器,大概意思是要生成一个唯一的标识。该函数返回了一个 RCTMethodInfo
类型的对象, 包含了导出函数信息,有 JS 名、原生函数名、是否为同步函数。
这些方法定义好后,是如何被 JS 所加载的,那我们继续深入源码,探究一下 CalendarManager
原生模块,是如何被 ReactNative 加载并被 JS 调用到的。
回到 -start
方法的第三步,加载原生模块:
// RCTCxxBridge.mm
- (void)start
{
dispatch_group_t prepareBridge = dispatch_group_create();
// 加载手动导出的原生模块
[self registerExtraModules];
// 加载自动导出的原生模块
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 加载调试模式所需原生模块
[self registerExtraLazyModules];
...
}
复制代码
我们以自动注册的模块为例,探究下具体实现部分:
// RCTCxxBridge.mm
- (NSArray<RCTModuleData *> *)_registerModulesForClasses:(NSArray<Class> *)moduleClasses
lazilyDiscovered:(BOOL)lazilyDiscovered
{
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways,
@"-[RCTCxxBridge initModulesWithDispatchGroup:] autoexported moduleData", nil);
NSArray *moduleClassesCopy = [moduleClasses copy];
NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
for (Class moduleClass in moduleClassesCopy) {
if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
continue;
}
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
// Check for module name collisions
RCTModuleData *moduleData = _moduleDataByName[moduleName];
if (moduleData) {
...省略一些代码
}
moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
_moduleDataByName[moduleName] = moduleData;
[_moduleClassesByID addObject:moduleClass];
[moduleDataByID addObject:moduleData];
}
[_moduleDataByID addObjectsFromArray:moduleDataByID];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
return moduleDataByID;
}
复制代码
这里面主要创建了三个表:
- _moduleDataByID数组:通过 RCTGetModuleClasses() 将前面注册到数组中的模块取出来,循环遍历创建RCTModuleData类型对象,存储到_moduleDataByID数组中。实际上RCTModuleData把原生模块包装了一层,原生模块的实例只是RCTModuleData中的一个属性。这里不仅实例化了原生模块,还做了一些其他事情,这个后面会说
- _moduleDataByName字典:key就是前面提到的,模块导出宏自动添加的 +moduleName 返回值,如果没有设置,默认为当前类名。
- _moduleClassesByID数组:将所有模块的Class装进数组。
到这里所有原生模块就都被 ReactNative 加载并初始化完毕。这些原生模块都是JS侧通过Bridge来进行调用,JSBridge
应该都是耳熟能详的东西了,专门用来做 JS 和原生进行交互使用的工具,不管 Cordova
框架 也好还是我前面几篇文章说的 WKJavaScriptBridge
也好,都是使用的这种桥接技术,万变不离其宗,ReactNative 也是通过 JSBridge
来做交互的,只不过是个 cpp
版的 bridge。这个 bridge 的构建还得从上面的 -start
说起。-start
函数在第五步里面初始化了底层Bridge,我们在深入探究下底层 Bridge 的初始化流程。
底层Bridge构建
-start
中关于底层 bridge 构建的相关代码如下:
// RCTCxxBridge.mm
- (void)start
{
// 创建JSThread
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
_jsThread.stackSize *= 2;
#endif
[_jsThread start];
dispatch_group_t prepareBridge = dispatch_group_create();
...
_reactInstance.reset(new Instance);
__weak RCTCxxBridge *weakSelf = self;
...
dispatch_group_enter(prepareBridge);
// 在_jsThread中初始化底层bridge
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
...
}
复制代码
-start
的时候,创建了一个 JS 线程_jsThread
,这里涉及到线程保活的知识,子线程默认不开启 runloop,子线程在执行完任务后会被释放掉,所以这里在子线程开启了个 runloop,让线程一直存活,底层 Bridge 初始化,js bundle 包的加载/运行都在 JS 线程执行。- 在
_jsThread
线程中构建底层bridge。
继续看 _initializeBridge:
都做了什么:
// RCTCxxBridge.mm
- (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
__weak RCTCxxBridge *weakSelf = self;
_jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
});
...
[self _initializeBridgeLocked:executorFactory];
}
- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
_reactInstance->initializeBridge(
std::make_unique<RCTInstanceCallback>(self),
executorFactory,
_jsMessageThread,
[self _buildModuleRegistryUnlocked]);
}
复制代码
- 创建一个名为
_jsMessageThread
线程,绑定了_jsThread
的 runloop,使其任务执行完毕不退出,被RCTCxxBridge
持有,传递给底层 bridge。 - 初始化底层bridge,这是一个
C++
函数,函数内部初始化了一个叫NativeToJsBridge
的实例。
// Instance.cpp
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry) {
callback_ = std::move(callback);
moduleRegistry_ = std::move(moduleRegistry);
jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
nativeToJsBridge_ = std::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
CHECK(nativeToJsBridge_);
}
复制代码
根据代码不难看出,最主要的工作是在上面传进来的线程中,同步执行 NativeToJsBridge
的创建,nativeToJsBridge_
的创建传递了三个参数:
-
InstanceCallback:底层调用结束后给上层的回调。
-
JSExecutorFactory:生产环境实际为
JSIExecutor
,后面这个类会细说。 -
MessageQueueThread:上层传递过来的
_jsMessageThread
。 -
ModuleRegistry:它负责两个非常核心的任务:一是生成中间版本的原生模块配置信息,进一步加工就可以最终导入给 JS 端,二是作为JS call Native的中转站。
ModuleRegistry
上面说了 moduleRegistry 中包括了所有native module信息,具体还要回到 RCTCxxBridge.mm 看代码:
// RCTCxxBridge.mm
- (std::shared_ptr<ModuleRegistry>)_buildModuleRegistryUnlocked
{
...
auto registry = std::make_shared<ModuleRegistry>(
createNativeModules(_moduleDataByID, self, _reactInstance),
moduleNotFoundCallback);
...
return registry;
}
复制代码
//RCTCxxUtils.mm
std::vector<std::unique_ptr<NativeModule>> createNativeModules(NSArray<RCTModuleData *> *modules, RCTBridge *bridge, const std::shared_ptr<Instance> &instance)
{
std::vector<std::unique_ptr<NativeModule>> nativeModules;
for (RCTModuleData *moduleData in modules) {
if ([moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
nativeModules.emplace_back(std::make_unique<CxxNativeModule>(
instance,
[moduleData.name UTF8String],
[moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; },
std::make_shared<DispatchMessageQueueThread>(moduleData)));
} else {
nativeModules.emplace_back(std::make_unique<RCTNativeModule>(bridge, moduleData));
}
}
return nativeModules;
}
复制代码
_buildModuleRegistryUnlocked
主要负责构建并返回一个 ModuleRegistry
实例,_moduleDataByID
正是前面初始化原生模块时候的 RCTModuleData
数组,这里遍历数组,将 RCTModuleData
模块生成对应的 cpp
版本的 NativeModule
,在 ModuleRegistry
的构造函数里,这些 NativeModule
又会被 ModuleRegistry
所持有:
ModuleRegistry::ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules, ModuleNotFoundCallback callback)
: modules_{std::move(modules)}, moduleNotFoundCallback_{callback} {}
复制代码
这个 modules_
至关重要,将来 JS 侧想要获取 Native 侧提供的各种配置信息都要靠它来获取。既然 ModuleRegistry 里面装的都是 NativeModule,那再看看 NativeModule 职能。NativeModule
定义了一套接口用于获取原生模块信息、调用原生模块函数:
// NativeModule.h
class NativeModule {
public:
virtual ~NativeModule() {}
//获取模块信息
virtual std::string getName() = 0;
virtual std::vector<MethodDescriptor> getMethods() = 0;
virtual folly::dynamic getConstants() = 0;
//调用原生函数
virtual void invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) = 0;
virtual MethodCallResult callSerializableNativeHook(unsigned int reactMethodId, folly::dynamic&& args) = 0;
};
复制代码
NativeModule
只提供了接口并没有实现,我把它理解成为一个接口,真正的实现是在 RCTNativeModule
,实际上就是个 C++ 版本的原生模块描述类,它是对 OC 版本 RCTModuleData
的封装,是面向底层 cpp 层 bridge 的。
获取模块信息,我们以 getConstants
为例,看名字也能看出来,这是在获取模块的常量信息。我们刨根问题往下走看看它是怎么拿到常量信息的:
// RCTNativeModule.mm
folly::dynamic RCTNativeModule::getConstants() {
...
NSDictionary *constants = m_moduleData.exportedConstants;
folly::dynamic ret = convertIdToFollyDynamic(constants);
return ret;
}
复制代码
m_moduleData 就是我们 OC 版本的原生模块信息 RCTModuleData,我们继续往下走:
// RCTModuleData.mm
- (NSDictionary<NSString *, id> *)exportedConstants
{
[self gatherConstants];
NSDictionary<NSString *, id> *constants = _constantsToExport;
_constantsToExport = nil; // Not needed anymore
return constants;
}
复制代码
- (void)gatherConstants
{
//如果有常量导出并且是第一次导出
if (_hasConstantsToExport && !_constantsToExport) {
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass]), nil);
//确保原生模块存在
(void)[self instance];
//因为整个bridge的构建和js与native的通信都是在jsthread中进行,所以如果需要在主线程获取constantsToExport需要切换回主线程
if (_requiresMainQueueSetup) {
if (!RCTIsMainQueue()) {
RCTLogWarn(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass);
}
RCTUnsafeExecuteOnMainQueueSync(^{
self->_constantsToExport = [self->_instance constantsToExport] ?: @{};
});
} else {
_constantsToExport = [_instance constantsToExport] ?: @{};
}
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
}
复制代码
经过上面的步骤我们不难发现,我们的原生module被 RCTModuleData
包了一层,RCTModuleData 又被 RCTNativeModule
包了一层,RCTNativeModule 又都装到了 ModuleRegistry
里,实际上 ModuleRegistry 也同样被更底层的 JSINativeModules
持有着,而 JSINativeModules 又被更更底层的 JSIExcutor
持有着,CalendarManager <- RCTModuleData <- RCTNativeModule <- ModuleRegistry <- JSINativeModules <- JSIExcutor
, JSIExcutor 会在执行 JS 代码的时候向 JS 侧注入一个叫 NativeModuleProxy 的对象,JS 侧调用到了这个对象,就会调用到Native侧的 JSIExcutor 内,这样我们原生模块的信息是如何提供给 JS 侧使用的路线就出来了。后面还会详细的说 JSIExcutor
NativeModuleProxy
JSINativeModules
。对 ReactNative源码没有过了解的同学可能有点迷糊,不过不用担心,后面说到 NativeModuleProxy
的时候,我会再用函数调用栈的形式再来一遍。
至此我们所有原生的模块信息都已经准备完毕,目前来看所有的原生模块都注册到了 ModuleRegistry
中,那不妨猜想以下,是不是只要 JS 调用了 ModuleRegistry
就可以执行原生模块的方法了?事实确实如此,要想理解这个调用过程,需要先了解一下 NativeToJsBridge
,NativeToJsBridge
是一 个C++ 类,前面说到的 Instance
是对 NativeToJsBridge
的一层包装,NativeToJsBridge
比 Instance
更接近底层。
NativeToJsBridge
NativeToJsBridge
顾名思义是 Native Call JS 的集大成者,那么它和JS Call Native有什么关系呢,实际上它内部还持有着 JSExecutor
和 JsToNativeBridge
,并将 JsToNativeBridge
最终传递给 JSIExecutor
,由 JSIExecutor
来触发JS Call Native。实际 Native Call JS
底层也是 JSIExecutor
实现,这个 JSIExecutor
具体是做什么的一直没说,这个先按住不说,后面再说,先看看 NativeToJsBridge
的定义:
// NativeToJsBridge.cpp
class NativeToJsBridge {
public:
friend class JsToNativeBridge;
// 必须在主线程调用
NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback);
virtual ~NativeToJsBridge();
// 传入module ID、method ID、参数用于在JS侧执行一个函数
void callFunction(std::string&& module, std::string&& method, folly::dynamic&& args);
// 通过callbackId调用JS侧的回调
void invokeCallback(double callbackId, folly::dynamic&& args);
// 开始执行JS application. 如果bundleRegistry非空,就会使用RAM的方式 读取JS源码文件
// 否则就假定 startupCode 已经包含了所有的JS源码文件
void loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);
void loadApplicationSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);
private:
std::shared_ptr<JsToNativeBridge> m_delegate;
std::unique_ptr<JSExecutor> m_executor;
std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;
};
复制代码
因为在模块加载那里举的例子并没有包含Native给JS的回调,实际上从上面的代码能看出来,如果有回调的话会触发 NativeToJsBridge 的 invokeCallback 方法,把 callbackId 回调给JS侧,JS 侧拿到 callbackId 就可以执行相应的回调了。先简单介绍下它的成员变量和方法:
成员变量
- m_delegate:JsToNativeBridge类型的引用,主要用于JS call Native
- m_executor:这个是在前面说的工厂创建出来的类,生产环境是JSIExecutor,调试环境是RCTObjcExecutor,这是底层通信的集大成者,不管是JS Call Native还是Native Call JS都是基于它来进行。
- m_executorMessageQueueThread:并非 NativeToJsBridge 自己初始化,而是作为初始化参数传递进来的,是最外层的
_jsMessageThread
。NativeToJsBridge 在被 Instance 初始化的时候传递进来的,JS 侧和 Native 侧交互都是在这个线程处理。
主要方法
-
void callFunction(std::string&& module, std::string&& method, folly::dynamic&& args); 这个函数的意义就是通过module ID和method ID以及参数去调用JS方法
-
void invokeCallback(double callbackId, folly::dynamic&& args); 这个函数的意义就是通过callbackId和参数触发一个JS的回调。通常是JS call Native method之后,native把一些异步的执行结果再以callback的形式回调给JS。
-
void loadApplication( std::unique_ptr bundleRegistry, std::unique_ptr startupCode, std::string sourceURL); 这个方法的作用是执行JS代码,他还有一个兄弟叫做 loadApplicationSync,顾名思义,他兄弟是一个同步函数,所以他自己就是异步执行JS代码。
构造函数
// NativeToJsBridge.cpp
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory *jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false)),
m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
m_executorMessageQueueThread(std::move(jsQueue)),
m_inspectable(m_executor->isInspectable()) {}
复制代码
NativeToJsBridge
内部把原生信息 registry
和外部传入的 callback
作为入参生成了 JsToNativeBridge
并持有, 这也是 JsToNativeBridge
能够 Call Native 的原因,jsExecutorFactory 又通过JsToNativeBridge 和外部传入的js线程生成了一个 executor 并持有,在生产环境下,这个executor就是 JSIExecutor。JS Call Native 实际上就是 JSIExecutor 来调用 JsToNativeBridge 实现,JSIExecutor 上面也有提到一直按住没表,那我们再来看下JSIExecutor。
JSIExecutor
通过上面的流程,我们明白了 JSIExecutor
相比 NativeToJsBridge
显得更底层,我们可以理解为 Instance
是 NativeToJsBridge
的包装,NativeToJsBridge
是 JSIExecutor
的包装,实际上在 Instance
中调用 callJSFunction:
,它的调用顺序应该是这样的:Instance->NativeToJsBridge->JSIExecutor
,JSIExecutor
会调用更底层,这个后面说。同样JS想要调用Native,也是一样:JSIExecutor->JsToNativeBridge->ModuleRegistry
。之前说了,在 NativeToJsBridge 的构造函数中 jsExecutorFactory 使用 JsToNativeBridge 实例 m_delegate 和 jsQueue 创建了 m_executor。这里我们主要以生产环境的JSIExecutor为例介绍。调试模式下请参考RCTObjcExecutor,他们都继承自 JSExecutor。下面是两种环境下 executor 的创建方式,生产环境的 JSIExecutor 通过 JSCExecutorFactory 生产,如下:
// JSCExecutorFactory.mm
return std::make_unique<JSIExecutor>(
facebook::jsc::makeJSCRuntime(),
delegate,
JSIExecutor::defaultTimeoutInvoker,
std::move(installBindings));
复制代码
了解了它的由来后我们再简要分析下 JSIExecutor
里面的几个关键属性:
// JSIExecutor.h
class JSIExecutor : public JSExecutor {
...省略很多代码
std::shared_ptr<jsi::Runtime> runtime_;
std::shared_ptr<ExecutorDelegate> delegate_;
JSINativeModules nativeModules_;
...
};
复制代码
- jsi::Runtime runtime_:实际上就是JSCRuntime,内部实现了Runtime的接口,提供了可以执行JS的能力,内部比较复杂,这是基于C语言版的JavaScriptCore实现,用于创建JS上下文,执行JS,像JS注入原生对象等功能。
- delegate_:这个delegate是在NativeToJsBridge中初始化JSIExecutor是传递进来的参数,这个参数正是JsToNativeBridge对象,负责JS Call Native。
- nativeModules_:由外部传入的 ModuleRegistry 构造而成,在JS Call Native的时候,会通过 JSINativeModules 里面的 ModuleRegistry 来获取原生模块信息,并把这个信息通过 __fbGenNativeModule 函数传递给 JS 侧,由此可见,原生模块信息并不是主动导入到 JS 侧的,而是 JS 侧到原生获取的,大致流程如下,首先从 Native 侧的
getModule
开始:
// JSINativeModules.cpp
Value JSINativeModules::getModule(Runtime& rt, const PropNameID& name) {
...
// 调用createModule方法
auto module = createModule(rt, moduleName);
...
auto result =
m_objects.emplace(std::move(moduleName), std::move(*module)).first;
return Value(rt, result->second);
}
复制代码
getModule
内部调用了 createModule
,那么 createModule 方法做了什么呢:
// JSINativeModules.cpp
folly::Optional<Object> JSINativeModules::createModule(
Runtime& rt,
const std::string& name) {
...
if (!m_genNativeModuleJS) {
// 获取JS侧全局函数__fbGenNativeModule
m_genNativeModuleJS =
rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
}
// 根据模块名,去原生模块注册对象中取出模块信息
auto result = m_moduleRegistry->getConfig(name);
...
// 调用__fbGenNativeModule并把原生模块信息传递过去
Value moduleInfo = m_genNativeModuleJS->call(
rt,
valueFromDynamic(rt, result->config),
static_cast<double>(result->index));
...
return module;
}
复制代码
- 获取JS侧全局函数
__fbGenNativeModule
,用以调用这个函数可以调用到 JS 侧,这个函数的具体细节,后面会讲。 - 根据模块名,去
ModuleRegistry
中取出模块信息,ModuleRegistry
的定义上面讲过了,所有原声模块的信息都存在这里。 - 调用
__fbGenNativeModule
这个全局函数并把从 ModuleRegistry 取出来的原生模块信息传递过去。
那么 getModule
是哪里调用的呢?
NativeModuleProxy
NativeModuleProxy 是 getModule
的唯一入口:
// JSIExecutor.cpp
class JSIExecutor::NativeModuleProxy : public jsi::HostObject {
public:
// 构造函数 JSIExecutor实例作为NativeModuleProxy构造函数的入参
NativeModuleProxy(JSIExecutor &executor) : executor_(executor) {}
// NativeModuleProxy 的 get方法 用于获取native module信息
Value get(Runtime &rt, const PropNameID &name) override {
return executor_.nativeModules_.getModule(rt, name);
}
};
复制代码
那么 NativeModuleProxy
这个 cpp 类又是在哪里使用的呢?全局搜索 NativeModuleProxy,你会发现只有一个地方再使用 NativeModuleProxy,就是 JSIExecutor 的 loadApplicationScript 方法,源码如下:
// JSIExecutor.cpp
void JSIExecutor::loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) {
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(*this)));
// ...
}
复制代码
runtime 是一个 JSCRuntime 类型对象,通过调用 rumtime_->global()
获得一个全局的 global 对象。然后又通过 setProperty
方法给 global 对象设置了一个名为 nativeModuleProxy
的对象。JS 侧的 global 对象通过 "nativeModuleProxy"
这个名字即可访问到 native 侧的 NativeModuleProxy。本质上,JS侧的 global.nativeModuleProxy
就是native侧的 nativeModuleProxy
。换句话说,我们在 JS 侧的 NativeModules 对应的就是 native 侧的 nativeModuleProxy
。我们不妨再深入看以下 JS 侧的源码:
let NativeModules: {[moduleName: string]: Object, ...} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else if (!global.nativeExtensions) {
const bridgeConfig = global.__fbBatchedBridgeConfig;
invariant(
bridgeConfig,
'__fbBatchedBridgeConfig is not set, cannot invoke native modules',
);
...
}
复制代码
我们在写 ReactNative 代码时使用的 NativeModules 正是原生端的 nativeModuleProxy 对象,文章开头的例子 NativeModules.CalendarManager
,实际上都会先到 Native 侧的 nativeModuleProxy,再到 JSINativeModules,再调用到 getModule 方法,通过 CalendarManager 模块名来获取 CalendarManager 的信息,然后 Call 全局的 JS 函数 __fbGenNativeModule
,把原生信息传递给 JS 侧。
原生模块信息导出
下面我们再以函数调用栈的形式,走一遍 CalendarManager 导出常量的流程,也可以理解为 JS 侧获取 Native 侧信息的流程,方法的导出同理:
- NativeModules-CalendarManager (JS侧)
- NativeModuleProxy-get (进入Native侧)
- JSINativeModules-getModule
- JSINativeModules-createModule
- ModuleRegistry-getConfig
- RCTNativeModule-getConstants
- RCTModuleData-exportedConstants
- CalendarManager-constantsToExport (进入自定义的CalendarManager)
- RCTModuleData-exportedConstants
- RCTNativeModule-getConstants
- ModuleRegistry-getConfig
我们可以顺便再分析下 JS 侧拿到 Native 侧的配置信息后做了什么:
// react-native/Libraries/BatchedBridge/NativeModules.js
// 生成原生模块信息
function genModule(
config: ?ModuleConfig,
moduleID: number,
): ?{name: string, module?: Object} {
const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
const module = {};
// 添加 JS版原生模块函数
methods &&
methods.forEach((methodName, methodID) => {
const isPromise =
promiseMethods && arrayContains(promiseMethods, methodID);
const isSync = syncMethods && arrayContains(syncMethods, methodID);
const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
// 生成JS函数
module[methodName] = genMethod(moduleID, methodID, methodType);
});
// 添加原生模块导出常量
Object.assign(module, constants);
if (module.getConstants == null) {
module.getConstants = () => constants || Object.freeze({});
} else {
...
}
return {name: moduleName, module};
}
// 导出genModule到全局变量global上以便native可以调用
global.__fbGenNativeModule = genModule;
// 生成函数
genMethod(moduleID: number, methodID: number, type: MethodType) {
let fn = null;
if (type === 'promise') {
fn = function(...args: Array<any>) {
return new Promise((resolve, reject) => {
// 函数入队
BatchedBridge.enqueueNativeCall(
moduleID,
methodID,
args,
data => resolve(data),
errorData => reject(createErrorFromErrorData(errorData)),
);
});
};
} else if (type === 'sync') {
fn = function(...args: Array<any>) {
return global.nativeCallSyncHook(moduleID, methodID, args);
};
} else {
fn = function(...args: Array<any>) {
...
BatchedBridge.enqueueNativeCall(...);
};
}
fn.type = type;
return fn;
}
复制代码
最后这个 return {name: moduleName, module}
会返回到原生端,原生端会取出 module,再将它构造成 JS 对象给 JS 使用:
Value moduleInfo = m_genNativeModuleJS->call(
rt,
valueFromDynamic(rt, result->config),
static_cast<double>(result->index));
CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";
folly::Optional<Object> module(
moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));
复制代码
到这里 JSIExecutor
的初始化完成了,JSBridge 就算搭建完了,以后 Native call JS 都会先后经由 Instance、NativeToJSBridge、JSIExecutor最终到达JS。
加载JS
分析到这里,原生模块已全部加载完毕,也已将所有原生模块信息导出给 JS 侧使用,RCTCxxBridge 到 _reactInstance 到 instance 背后的 NativeToJsBridge、JSIExecutor 构建 Bridge 的整个流程也已经全部走完。但是最后一步,JS是怎么调用到原生和原生怎么给JS回调的还并未展开,我们先回到 RCTCxxBrige 的 -start
方法:
- (void)start
{
// 异步加载 js bundle包
[self loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
} onProgress:^(RCTLoadingProgress *progressData) {
// 展示加载bundle 的 loadingView
...
}];
}
// 等待native moudle 和 JS 代码加载完毕后就执行JS
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
复制代码
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
{
...
__weak RCTCxxBridge *weakSelf = self;
[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onProgress:onProgress onComplete:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
return;
}
onSourceLoad(error, source);
}];
}
+ (void)loadBundleAtURL:(NSURL *)scriptURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)onComplete
{
// 尝试同步加载
int64_t sourceLength;
NSError *error;
NSData *data = [self attemptSynchronousLoadOfBundleAtURL:scriptURL
runtimeBCVersion:JSNoBytecodeFileFormatVersion
sourceLength:&sourceLength
error:&error];
if (data) {
onComplete(nil, RCTSourceCreate(scriptURL, data, sourceLength));
return;
}
...
// 同步加载失败异步加载
if (isCannotLoadSyncError) {
attemptAsynchronousLoadOfBundleAtURL(scriptURL, onProgress, onComplete);
} else {
onComplete(error, nil);
}
}
static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoadProgressBlock onProgress, RCTSourceLoadBlock onComplete)
{
scriptURL = sanitizeURL(scriptURL);
if (scriptURL.fileURL) {
// 异步加载
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSData *source = [NSData dataWithContentsOfFile:scriptURL.path
options:NSDataReadingMappedIfSafe
error:&error];
onComplete(error, RCTSourceCreate(scriptURL, source, source.length));
});
return;
}
}
复制代码
实际上 JS 代码的加载和底层 Bridge 的创建,都是并发执行的,因为 dispatch_group 的缘故,只有在他们都执行完毕和原生模块初始化完毕后,才会执行 JS。
执行JS
终于到了最后异步,执行JS代码,其实就是把js bundle包经过层层传递,最终交给 JavaScriptCore 去执行。
/ RCTCxxBridge.mm
- (void)start
{
...
// 等待”原生模块实例创建完毕、底层Bridge初始化完毕、js bundle包加载完毕“,在JS线程执行js源码
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
}
复制代码
最后通过executeSourceCode执行代码,executeSourceCode 源码如下:
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
{
// JS bundle包执行完毕回调
dispatch_block_t completion = ^{
// 执行暂存的Native call JS
[self _flushPendingCalls];
dispatch_async(dispatch_get_main_queue(), ^{
// 主线程发送通知
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptDidLoadNotification
object:self->_parentBridge userInfo:@{@"bridge": self}];
// 开启定时任务,最终用于驱动JS端定时任务
[self ensureOnJavaScriptThread:^{
[self->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]];
}];
});
};
// 根据sync来选择执行JS的方式(同步、异步)
if (sync) {
[self executeApplicationScriptSync:sourceCode url:self.bundleURL];
completion();
} else {
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
}
}
复制代码
上述代码主要做了两件事情:
- 构建回调,在JS执行完进行回调。
- 执行 JS 代码。
- (void)enqueueApplicationScript:(NSData *)script
url:(NSURL *)url
onComplete:(dispatch_block_t)onComplete
{
// 底层转化为在JS线程执行JS bundle
[self executeApplicationScript:script url:url async:YES];
if (onComplete) {
_jsMessageThread->runOnQueue(onComplete);
}
}
复制代码
由于底层js bundle最终会在JS线程执行,因此 _jsMessageThread->runOnQueue(onComplete)
可以保证先执行完 JS bundle,后执行 onComplete 回调。
- (void)executeApplicationScript:(NSData *)script
url:(NSURL *)url
async:(BOOL)async
{
[self _tryAndHandleError:^{
NSString *sourceUrlStr = deriveSourceURL(url);
...
// 根据 async 来同步加载、异步加载
self->_reactInstance->loadScriptFromString(std::make_unique<NSDataBigString>(script),sourceUrlStr.UTF8String, !async);
}];
}
复制代码
// Instance.cpp
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously) {
...
loadApplication(nullptr, std::move(string), std::move(sourceURL));
}
void Instance::loadApplication(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
nativeToJsBridge_->loadApplication(std::move(bundleRegistry), std::move(string),
std::move(sourceURL));
}
复制代码
// NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
// 派发到JS线程执行js bundle
runOnExecutorQueue( [...] (JSExecutor* executor) mutable {
...
executor->loadApplicationScript(std::move(*startupScript), std::move(startupScriptSourceURL));
});
}
复制代码
如上,loadApplication
和 loadApplicationSync
这两个方法实现基本一致,都是调用了成员变量 m_executor的loadApplicationScript
方法,区别在于 loadApplication
把代码放到了 m_executorMessageQueueThread
中去执行,而 loadApplicationSync
在当前线程执行。
// JSIexecutor.cpp
void JSIExecutor::loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) {
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(*this)));
runtime_->global().setProperty(
*runtime_,
"nativeFlushQueueImmediate",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) {
callNativeModules(args[0], false);
return Value::undefined();
}));
runtime_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return nativeCallSyncHook(args, count); }));
// 最终调用到JavaScriptCore的JSEvaluateScript函数
runtime_->evaluateJavaScript(
std::make_unique<BigStringBuffer>(std::move(script)), sourceURL);
flush();
}
复制代码
- 通过 setProperty 方法给 global 对象设置了 nativeModuleProxy 的对象。JS 侧的 NativeModules 对应的就是 native 侧的 nativeModuleProxy。
- 向 global 中注入了 nativeFlushQueueImmediate,nativeCallSyncHook 两个方法。
- 调用
runtime_->evaluateJavaScript
方法,最终调用到 JavaScriptCore 的JSEvaluateScript
函数,SEvaluateScript 的作用就是在 JS 环境中执行 JS 代码。 - JS 脚本执行完成,执行
flush
操作。flush 函数的主要作用就是执行 JS 侧的队列中缓存的对 native 的方法调用。
void JSIExecutor::flush() {
// 如果JSIExecutor的flushedQueue_函数不为空,则通过函数flushedQueue_获取待调用的方法queue,然后执行callNativeModules
if (flushedQueue_) {
callNativeModules(flushedQueue_->call(*runtime_), true);
return;
}
// 以"__fbBatchedBridge"作为属性key去global中取对应的值也就是batchedBridge,batchedBridge本质上是JS侧的MessageQueue类实例化的一个对象
Value batchedBridge =
runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
if (!batchedBridge.isUndefined()) {
// 绑定batchedBridge
bindBridge();
callNativeModules(flushedQueue_->call(*runtime_), true);
} else if (delegate_) {
// 如果没有获取到JS侧定义的batchedBridge对象,则直接执行callNativeModules方法,即没有bind操作。
callNativeModules(nullptr, true);
}
}
复制代码
BatchedBridge:批处理桥,ReactNative 在处理 JS 和 Native 通信并非调用一次执行一次,而是将调用消息保存到队列,在适当的实际进行flush。
flush()
flush
函数有必要着重讲一下,在 javaScript 代码被执行完毕后,会马上执行 flush() 函数,flush 内会先判断 flushedQueue_
是否存在,flushedQueue_
如果存在,就会直接执行 callNativeModules 调用原生模块的方法,如果不存在,就去 JS 侧获取 batchedBridge
,batchedBridge
是什么,js 侧有个 BatchedBridge.js
,如下:
// BatchedBridge.js
const MessageQueue = require('./MessageQueue');
const BatchedBridge: MessageQueue = new MessageQueue();
Object.defineProperty(global, '__fbBatchedBridge', {
configurable: true,
value: BatchedBridge,
});
module.exports = BatchedBridge;
复制代码
如果 JS 调用过 Native,BatchedBridge 就会被初始化,BatchedBridge 对象实际上就是 JS 侧的 MessageQueue,在初始化完之后,定义了一个叫 __fbBatchedBridge
的全局属性,并把 BatchedBridge 对象作为这个属性的 value 值,等待着被 JSIExecutor
使用。
再回到 flush()
方法里面,native 侧通过 __fbBatchedBridge
拿到 batchedBridge,先判断它是否存在,如果不存在,说明 JS 从来没有调用过 Native,callNativeModules 直接传空,如果存在,就说明 JS 侧有调用,Native 侧就需要进行绑定 JSBridge 的方法,将 JS 的调用队列拿过来进行执行。因为在 JS 被加载的过程中,可能会存在 JS 调用 Native,所以这里提前执行一次 flush
,将这些调用全部执行。
bindBridge()
flush()另一个重要的地方就是 bindBridge

相关文章
- OpenGL ES之旋转的地球
- Swift 5.x - 继承(中文文档)
- ⌈磨刀不误砍柴工⌋ WebStorm常用的配置和快捷键大全
- 实用的 bash 自定义脚本,快速提效工作流程
- mojito: 麻烦给我的爱人来一份 RxSwift
- WWDC20 第四弹 - Swift的新特性
- 面试官:给我说说什么是同步异步?
- 页面加载性能之使用WebP图片
下一篇: 如何用Swift 实现一个单链表?