ReactNative源码分析 - 启动流程
1.ReactNative源码分析 - 概述
2.ReactNative源码分析 - JavaScriptCore C语言篇
3.ReactNative源码分析 - 启动流程
4.ReactNative源码分析 - 通信机制
5.ReactNative源码分析 - 渲染原理
- 一、ReactNative类图
- 二、ReactNative启动流程简析
- 三、ReactNative启动流程详解
- 0.启动流程起点
- 1.构建Native&JS通信表层Bridge
- 2.创建JS线程
- 3.收集原生模块信息,构建原生模块实例
- 4.构建真正执行通信任务的底层Bridge
- 5.加载js bundle包
- 6.执行js bundle包
- 四、启动流程线程管理
一、ReactNative类图
- ReactNative类相对较多,下图为对应UML类图,列出了有助于梳理主流程的主要类。ReactNative启动流程的层次结构较深、涉及的类较多、代码调用从OC到OC/C++混编到纯C++公共层不断深入、阅读源码过程中可根据下图梳理主要类之间的关系。笔者阅读源码的使用(测试DEMO)中包含一些注释,可能会有助于理解一些细节。
二、ReactNative启动流程简析
-
探究ReactNative启动流程,可以通过断点调试追踪调用栈,一步步深入。启动流程主要做了几件事情:
- 1.构建Native&JS通信表层Bridge;
- 2.创建JS线程;
- 3.收集原生模块(<RCTBridgeModule>)信息,构建原生模块实例;
- 4.构建真正执行通信任务的底层Bridge;
- 5.加载js bundle包;
- 6.执行js bundle包;
-
ReactNative项目启动后存主要涉及两个线程:主线程、JS线程。以上步骤并非线性执行,涉及线程切换/等待。大部分初始化操作、原生模块实例创建在主线程执行;底层Bridge初始化JS线程执行,js bundle包的加载/运行都在JS线程执行。
-
下面详细分析启动流程,根据出场顺序先介绍各个核心类的主要功能,再从代码层面分析。
注意:本文只分析最常规的启动流程,即
Release版本
下的启动流程,加载本地
bundle整包
并执行,代码分析会有所省略,只留下主流程。可以手动打js bundle离线包并调整Xcode开发模式为Release,有需要的话可借助DEMO。
三、ReactNative启动流程详解
0.启动流程起点
ReactNative代码入口是:RCTRootView初始化函数initWithBridge: initialProperties:
,用于实例化RCTRootView作为UIViewController的view。其中用传入RCTBridge,这便是启动流程的源头,启动完毕才正在执行JS模块AppRegistry runApplication,运行组件,开始渲染。
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
{
if (self = [super initWithFrame:CGRectZero]) {
_bridge = bridge;
_moduleName = moduleName;
_appProperties = [initialProperties copy];
...
[self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];
}
return self;
}
- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
...
[self runApplication:bridge];
...
}
- (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
};
// (Native调用JS) 调用JS模块AppRegistry runApplication,运行组件
[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
}
1.构建Native&JS通信表层Bridge
- *RCTBridge是ReactNative暴露出来给用户使用的类,从接口可看出它对外主要负责:
- Bridge初始化;
- Native调用JS:即原生调用JS模块;
- 启动流程进度通知;
实际上它内部创建并持有RCTCxxBridge
,然后大部分操作是委托批量桥batchedBridge执行,即RCTCxxBridge才是真正的执行者。每个原生模块都会弱引用它,用于随时获取Bridge支持。
// RCTBridge.h
// 启动流程通知
RCT_EXTERN NSString *const RCTJavaScriptWillStartLoadingNotification;
RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification;
...
// 初始化
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
launchOptions:(NSDictionary *)launchOptions;
...
// 调用JS模块函数
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion
// RCTBridge.m
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
bundleURL:(NSURL *)bundleURL
moduleProvider:(RCTBridgeModuleListProvider)block
launchOptions:(NSDictionary *)launchOptions
{
if (self = [super init]) {
_delegate = delegate;
_bundleURL = bundleURL;
_moduleProvider = block;
_launchOptions = [launchOptions copy];
[self setUp]; // 初始化batchedBridge
}
return self;
}
- (void)setUp
{
Class bridgeClass = self.bridgeClass;
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
...
// 构建batchedBridge(C++ Bridge)
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
[self.batchedBridge start];
}
- *RCTCxxBridge(即C++Bridge、也称batchedBridge)是ReactNative启动流程的主要管理者,启动流程主要步骤、线程管理都是由它控制。主要负责:
- 创建并持有JS线程:jsThread;
- 收集并持有所有原生模块(RCTBridgeModule)信息,构建原生模块实例;
- 创建并持有真正执行通信任务的底层Instance以及其他一系列底层模块;
- 加载js bundle包;
- 等待一切就绪,执行js bundle包;
RCTCxxBridge初始化主要是创建一些标识位、原生模块容器等。接着执行start操作,这是启动流程的最为核心的地方
,启动流程所有操作都从这里开始。
// RCTCxxBridge.mm
- (void)start
{
// 2.创建常驻线程,开启runloop
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
[_jsThread start];
dispatch_group_t prepareBridge = dispatch_group_create();
// 3.加载自动注册的原生模块(创建原生模块描述对象、构造原生模块实例)
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 4.js线程中初始化底层 _reactInstance
// 创建底层 _reactInstance实例
_reactInstance.reset(new Instance);
std::shared_ptr<JSExecutorFactory> executorFactory;
executorFactory = std::make_shared<JSCExecutorFactory>(nullptr); // 生产环境工厂类 JSCExecutorFactory
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
// 主要初始化操作在initializeBridge中完成
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 5.加载 js bundle包(整包,异步加载)
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) { ... }];
// 6.等待 需要在主线程创建的原生模块实例创建完毕、底层_reactInstance初始化完毕、 js bundle包加载完毕,执行js bundle
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];
}
});
}
2.创建JS线程
JS线程:这是一个常驻线程,会一直存活,涉及RunLoop线程保活相关知识。启动过程中的很多初始化操作、Native&JS通信、执行js bundle包都在该线程进行。
// RCTCxxBridge.mm
- (void)start
{
// 2.创建常驻线程,开启runloop
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
[_jsThread start];
...
}
+ (void)runRunLoop
{
@autoreleasepool {
pthread_setname_np([NSThread currentThread].name.UTF8String);
// 添加事件源Source,避免退出RunLoop
CFRunLoopSourceContext noSpinCtx = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
CFRunLoopSourceRef noSpinSource = CFRunLoopSourceCreate(NULL, 0, &noSpinCtx);
CFRunLoopAddSource(CFRunLoopGetCurrent(), noSpinSource, kCFRunLoopDefaultMode);
CFRelease(noSpinSource);
// kCFRunLoopDefaultMode模式下启动RunLoop,永不退出
while (kCFRunLoopRunStopped != CFRunLoopRunInMode(kCFRunLoopDefaultMode, ((NSDate *)[NSDate distantFuture]).timeIntervalSinceReferenceDate, NO)) {
RCTAssert(NO, @"not reached assertion"); // runloop spun. that's bad.
}
}
}
-
ensureOnJavaScriptThread
把任务派发到JS线程执行
// RCTCxxBridge.mm
- (void)ensureOnJavaScriptThread:(dispatch_block_t)block
{
if ([NSThread currentThread] == _jsThread) {
[self _tryAndHandleError:block];
} else {
[self performSelector:@selector(_tryAndHandleError:)
onThread:_jsThread
withObject:block
waitUntilDone:NO];
}
}
3.收集原生模块信息,构建原生模块实例
-
小技巧:
借助Xcode的预编译操作来做宏替换,助于理解可读性较差的宏。操作流程是:选中想要执行预编译的文件,Xcode => Product => Perform Action => Preprocess “XXX.m”。文件过大可能无法转化
-
原生模块的导出原理
分析原生模块收集流程之前,先梳理原生模块的导出原理。导出到JS端使用的原生模块都遵守RCTBridgeModule协议,通常通过宏RCT_EXPORT_MODULE
来导出。
RCT_EXPORT_MODULE(TestManager)
// 预处理后代码如下
extern void RCTRegisterModule(Class);
+ (NSString *)moduleName {
return @"TestManager";
}
+ (void)load {
RCTRegisterModule(self);
}
RCT_EXPORT_MODULE
宏替换结果如上,主要是:实现RCTBridgeModule协议函数+ (NSString *)moduleName
返回原生模块名称,并重写+ (void)load
函数,调用RCTRegisterModule()
把类注册到原生模块类集合。通过函数RCTGetModuleClasses()
可获取
// RCTBridge.m
// 原生模块表
static NSMutableArray<Class> *RCTModuleClasses;
// 原生模块表操作队列:保证读写安全(读写锁:多读一写,不同时读写)
static dispatch_queue_t RCTModuleClassesSyncQueue;
NSArray<Class> *RCTGetModuleClasses(void)
{
__block NSArray<Class> *result;
dispatch_sync(RCTModuleClassesSyncQueue, ^{
result = [RCTModuleClasses copy];
});
return result;
}
void RCTRegisterModule(Class moduleClass)
{
...
dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
[RCTModuleClasses addObject:moduleClass];
});
}
原生模块函数通过宏RCT_EXPORT_METHOD
来导出,宏替换效果如下。可知该宏主要作用是:生成导出函数,并且生成一个以__rct_export__
为前缀,加上当前行号列号作为函数名的类函数,该函数返回的结构体RCTMethodInfo
包含了导出函数信息有 JS名、原生函数名、是否为同步函数。后序收集原生模块信息正是通过runtime扫描带__rct_export__
前缀的函数,执行函数调用以获取导出函数信息。
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSNumber *)secondsSinceUnixEpoch)
{
NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
RCTLogInfo(@"Date : %@",date);
}
// 上述导出函数宏替换后如下
+ (const RCTMethodInfo *)__rct_export__390 {
static RCTMethodInfo config = {
"",
"addEvent:(NSString *)name location:(NSString *)location date:(NSNumber *)secondsSinceUnixEpoch",
NO,
};
return &config;
}
- (void)addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)secondsSinceUnixEpoch ;
{
NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
RCTLogInfo(@"Date : %@",date);
}
- RCTModuleData是原生模块描述对象,包含原生模块的一切信息,包括原生模块名称、导出函数、导出常量、函数执行线程等,原生模块对象的实例化也由它管理。基于懒加载机制,每个原生模块都是一个单例,只会创建一次。
初始化:RCTModuleData初始化仅需入原生模块类型moduleClass、bridge,但已足够构建原生模块实例并获取它的一切信息
// RCTModuleData.mm
- (instancetype)initWithModuleClass:(Class)moduleClass
bridge:(RCTBridge *)bridge
{
// 初始化 moduleProvider,用于后面构建模块实例_instance
return [self initWithModuleClass:moduleClass
moduleProvider:^id<RCTBridgeModule>{ return [moduleClass new]; }
bridge:bridge];
}
构建原生模块实例:使用c++互斥锁std::mutex
保证实例的线程安全。unique_lock()函数,独占or等待互斥锁_instanceLock,当lock作用域结束时,自动释放锁。 {...} 为加锁区,作用是减小加锁区域。
// RCTModuleData.mm
- (void)setUpInstanceAndBridge {
{ // 加锁区域
std::unique_lock<std::mutex> lock(_instanceLock);
if (!_setupComplete && _bridge.valid) {
if (!_instance) {
// 构建实例
_instance = _moduleProvider ? _moduleProvider() : nil;
}
....
[self setBridgeForInstance];
}
[self setUpMethodQueue];
}
...
}
// 配置原生模块实例bridge RCTCxxBridge
- (void)setBridgeForInstance
{
if ([_instance respondsToSelector:@selector(bridge)] && _instance.bridge != _bridge) {
[(id)_instance setValue:_bridge forKey:@"bridge"];
}
}
获取导出函数:Runtime遍历原生模块类函数,找出以__rct_export__
为前缀的并调用,以获取导出函数信息RCTMethodInfo
,构造RCTBridgeMethod
实例。RCTBridgeMethod
实例是原生模块导出函数描述对象,包含导出函数信息,JS call Native最终由它来执行函数调用。
// RCTModuleData.mm
// 获取模块导出的方法
- (NSArray<id<RCTBridgeMethod>> *)methods
{
if (!_methods) {
NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];
// Runtime遍历原生模块类方法,找出以__rct_export__为浅醉的类方法
unsigned int methodCount;
Class cls = _moduleClass;
while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
// 执行原生函数,获取导出函数信息
IMP imp = method_getImplementation(method);
auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_moduleClass, selector);
id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod moduleClass:_moduleClass];
[moduleMethods addObject:moduleMethod];
}
}
free(methods);
cls = class_getSuperclass(cls);
}
_methods = [moduleMethods copy];
}
return _methods;
}
-
收集原生模块信息,构建原生模块实例
回到正题,继续流程分析。原生模块导出到Bridge有多种方式,此处分析通过自动注册导出(通过RCT_EXPORT_MODULE导出)的原生模块。原生模块导出包括两个步骤:- 1.通过
RCTGetModuleClasses()
函数获取原生模块集合,并创建模原生块描述对象集合<RCTModuleData>。在主线程执行。 - 2.实例化(instance)需要在主线程Setup的原生模块。实现相应协议
requiresMainQueueSetup
、有导出常量…会在主线程实例化。
- 1.通过
// RCTCxxBridge.mm
- (void)start
{
···
dispatch_group_t prepareBridge = dispatch_group_create();
// 3.加载自动注册的原生模块(创建原生模块描述对象、构造原生模块实例)
// 加载手动导出的原生模块
[self registerExtraModules];
// 加载自动注册的原生模块(创建原生模块描述对象、构造原生模块实例)
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 加载调试模式所需原生模块
[self registerExtraLazyModules];
···
}
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<id<RCTBridgeModule>> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
// 创建原生模块描述对象
NSArray<RCTModuleData *> *moduleDataById = [self _registerModulesForClasses:modules lazilyDiscovered:lazilyDiscovered];
...
// 创建 自动注册的原生模块实例
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
- (NSArray<RCTModuleData *> *)_registerModulesForClasses:(NSArray<Class> *)moduleClasses
lazilyDiscovered:(BOOL)lazilyDiscovered
{
NSArray *moduleClassesCopy = [moduleClasses copy];
NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
for (Class moduleClass in moduleClassesCopy) {
···
// 创建原生模块描述对象,并存放到对应容器
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
_moduleDataByName[moduleName] = moduleData;
[_moduleClassesByID addObject:moduleClass];
[moduleDataByID addObject:moduleData];
}
[_moduleDataByID addObjectsFromArray:moduleDataByID];
}
- (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.requiresMainQueueSetup) {
dispatch_block_t block = ^{
if (self.valid && ![moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
// 创建模块实例
(void)[moduleData instance];
// 收集模块导出常量
[moduleData gatherConstants];
}
};
if (RCTIsMainQueue()) {
block();
} else {
if (dispatchGroup) {
dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
}
}
}
}
}
注:触发原生模块实例化的线程无法保证。如果是在JS线程触发需要在主线程实例化的原生模块
实例化,除非使用dispatch_sync同步函数派发实例化任务到主线程,否则不可以懒加载,但这种方式可能造成死锁(假设实例化任务在主线程发起…)。为此ReactNative使用GCD队列组,在加载js bundle的同时,在主线程构建原生模块实例,保证在执行js bundle之前,需要在主线程实例化的原生模块实例化完毕。
4.构建真正执行通信任务的底层Bridge
- 这个步骤有一匹布那么长,会构建出UML图中Instance以下的整个类体系,阅读源码需要一点点耐心,建议参照UML类图梳理各个类之间的关系。整个流程都在JS线程执行。
理解底层Bridge要抓住三个关键职能- 1.JS call Native
- 2.Native call JS
- 3.执行js bundle
// RCTCxxBridge.mm
- (void)start
{
...
// 4.在js线程中初始化底层 _reactInstance
// 创建底层 _reactInstance实例,主要初始化操作在是函数initializeBridge中完成
_reactInstance.reset(new Instance);
std::shared_ptr<JSExecutorFactory> executorFactory;
executorFactory = std::make_shared<JSCExecutorFactory>(nullptr); // 生产环境工厂类 JSCExecutorFactory
// JS线程 初始化底层Bridge
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
...
}
- (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
// 构建 _jsMessageThread
__weak RCTCxxBridge *weakSelf = self;
_jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) { ... });
if (_reactInstance) {
[self _initializeBridgeLocked:executorFactory];
}
}
- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
std::lock_guard<std::mutex> guard(_moduleRegistryLock);
_reactInstance->initializeBridge(
std::make_unique<RCTInstanceCallback>(self),
executorFactory,
_jsMessageThread,
[self _buildModuleRegistryUnlocked]);
}
- (std::shared_ptr<ModuleRegistry>)_buildModuleRegistryUnlocked
{
ModuleRegistry::ModuleNotFoundCallback moduleNotFoundCallback = ...
auto registry = std::make_shared<ModuleRegistry>(
createNativeModules(_moduleDataByID, self, _reactInstance),
moduleNotFoundCallback);
return registry;
}
Instance
初始化函数initializeBridge
,意味着已经到了ReactNative框架的公共层
(提及框架的分层设计,ReactCommon目录为iOS/Android两端公共层),代码为纯C++。此处传参出现了几个新类型,在此一一解释
-
RCTInstanceCallback实现公共层C++接口
InstanceCallback
,用于获取底层回调,最终通过RCTCxxBridge派发给原生模块(主要用于通知RCTUIManager触发渲染)。
struct RCTInstanceCallback : public InstanceCallback {
void onBatchComplete() override {
[bridge_ partialBatchDidFlush];
[bridge_ batchDidComplete];
}
};
-
RCTMessageThread实现公共层C++接口
MessageQueueThread
,它是对JS线程对应的RunLoop的封装,传递给底层用于底层派发任务到JS线程,支持同步/异步操作,这里使用信号量
来实现同步。
// RCTMessageThread.mm
// 往runloop中添加block任务
void RCTMessageThread::runAsync(std::function<void()> func) {
CFRunLoopPerformBlock(m_cfRunLoop, kCFRunLoopCommonModes, ^{ func(); });
CFRunLoopWakeUp(m_cfRunLoop);
}
// 往runloop中添加block任务(使用信号量 实现同步)
void RCTMessageThread::runSync(std::function<void()> func) {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
runAsync([func=std::make_shared<std::function<void()>>(std::move(func)), &sema] {
(*func)();
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
-
JSCExecutorFactory实现公共层C++接口
JSExecutorFactory
,使用工厂模式,在不同场景构建出不同的JSExecutor,其中生产环境使用JSCExecutorFactory
,用于构建JSIExecutor。
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) {
return folly::make_unique<JSIExecutor>(
facebook::jsc::makeJSCRuntime(), // 创建JS Runtime
delegate,
[](const std::string &message, unsigned int logLevel) { ... },
JSIExecutor::defaultTimeoutInvoker,
std::move(runtimeInstaller_));
}
-
RCTNativeModule实现公共层C++接口
NativeModule
。NativeModule
定义了一套接口用于获取原生模块信息、调用原生模块函数。RCTNativeModule实际上就是个C++版本的元生模块描述类,它是对OC版本的RCTModuleData
封装。
// 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;
};
// RCTCxxUtils.mm
// 构建C++原生模块描述对象RCTNativeModule集合
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) {
...
nativeModules.emplace_back(std::make_unique<RCTNativeModule>(bridge, moduleData));
}
return nativeModules;
}
-
Instance其实就是
NativeToJsBridge
的一层包装,它主要负责创建NativeToJsBridge
,并用它来调用JS模块函数、执行JS回调(原生模块导出函数支持带回调类型的参数RCTResponseSenderBlock,对应的JS回调会暂存在JS端,最终再由原生端调用才得以执行)、执行JS bundle包。
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 {
// js线程中 同步 初始化NativeToJsBridge
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
// 同步信号,初始化 NativeToJsBridge完毕,才允许loadApplicationSync
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
}
// 执行JS bundle
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));
}
// 调用JS模块函数
void Instance::callJSFunction(std::string &&module, std::string &&method,
folly::dynamic &¶ms) {
nativeToJsBridge_->callFunction(std::move(module), std::move(method),
std::move(params));
}
// 执行JS回调
void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &¶ms) {
nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
}
-
题外话
上述分析可发现很多类都是对公共层C++接口的实现,ReactNative架构分层设计,底层是iOS/Andriod共用的公共层,纯C++编写。这么设计笔者的理解是:- 1.跨平台的需要:Objective-C和JAVA总需要一个底层来衔接,跨平台并且面向对象的C++是不二之选;
- 2.把更多的处理逻辑下沉到C++层:这有助于提高代码复用率、提升性能。
当需要iOS/Android平台各自实现相应功能时,采用面向协议编程思想,在公共层定义接口标准,由两端分别实现,最终实现了分层设计的衔接。
-
ModuleRegistry是原生模块注册机,它持有所有原生模块信息(NativeModule集合),负责两个非常核心的任务:
- 生成中间版本的原生模块配置信息,进一步加工就可以最终导入JS端
- 作为JS call Native的中转站(JS&Native通信的调用链条比较长,详见通信机制篇)。
// ModuleRegistry.h
ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules, ModuleNotFoundCallback callback = nullptr);
// 生成原生模块配置信息,用于导入JS端
folly::Optional<ModuleConfig> getConfig(const std::string& name);
// 调用用原生模块函数 moduleId:模块索引,methodId:函数索引,params 参数
void callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId);
// 调用原生模块同步函数
MethodCallResult callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
private:
// C++版本的元原生模块集合 RCTNativeModule
std::vector<std::unique_ptr<NativeModule>> modules_;
-
NativeToJsBridge管理Native call JS,即原生模块调用JS模块的管理者,主要功能是
- 创建并持有JsToNativeBridge,用于管理JS call Native(该对象最终传递给JSIExecutor)。
- 使用JSExecutorFactory创建并持有JSIExecutor,用于管理 Native call JS、执行js bundle。
所以NativeToJsBridge的命名有点迷惑,其实它的主要工作是集成,通过JSIExecutor、JsToNativeBridge来实现三个关键职能。
// NativeToJsBridge.cpp
// 初始化,构建JsToNativeBridge、JSIExecutor
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)) {}
// 执行js bundle
void NativeToJsBridge::loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
....
}
// 调用JS模块函数
void NativeToJsBridge::callFunction(
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
...
}
// 执行JS回调
void NativeToJsBridge::invokeCallback(double callbackId, folly::dynamic&& arguments) { ... }
-
JsToNativeBridge,实现
ExecutorDelegate
接口。顾名思义用来管理JS call Native,底层最终是通过原生模块注册机ModuleRegistry
来发起调用,因为ModuleRegistry
才真正拥有所有原生模块信息。
// NativeToJsBridge.cpp
// 调用原生模块函数
void callNativeModules(
JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
// 解析函数调用元素,依次执行函数调用(JS call Native批量执行)
for (auto& call : parseMethodCalls(std::move(calls))) {
m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
...
}
// 同步调用原生模块函数
MethodCallResult callSerializableNativeHook(
JSExecutor& executor, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& args) override {
return m_registry->callSerializableNativeHook(moduleId, methodId, std::move(args));
}
-
JSIExecutor即JS执行者,它是底层真正的集大成者,最终三个关键职能都是由它来管理,主要负责
- 持有
jsi::Runtime
,实际实现是JSCRuntime
。JSCRuntime是ReactNative对JavaScriptCore的一层面向对象的封装,底层所有的Native&JS交互都发生在这里。 - 管理Native call JS。
- 持有
ExecutorDelegate
,实际实现是JsToNativeBridge
,用于管理JS call Native。 - 运行js bundle包。
- 向JS端注入原生模块信息。
Native&JS交互的实现基于JavaScriptCore(JSCRuntime),需要Native端向JS端注入原生对象,具体实现后序会分析。
- 持有
// JSIExecutor.cpp
JSIExecutor::JSIExecutor(
std::shared_ptr<jsi::Runtime> runtime,
std::shared_ptr<ExecutorDelegate> delegate,
Logger logger,
const JSIScopedTimeoutInvoker& scopedTimeoutInvoker,
RuntimeInstaller runtimeInstaller)
: runtime_(runtime),
delegate_(delegate),
nativeModules_(delegate ? delegate->getModuleRegistry() : nullptr),
scopedTimeoutInvoker_(scopedTimeoutInvoker),
runtimeInstaller_(runtimeInstaller) {
}
// 执行js bundle
void JSIExecutor::loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) { ... }
// 调用JS模块函数
void JSIExecutor::callFunction(
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) { ... }
// 执行JS回调
void JSIExecutor::invokeCallback(
const double callbackId,
const folly::dynamic& arguments) { ... }
// 调用原生模块函数
void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {
// dynamicFromValue 函数调用元素 JS value 转 c++ 动态类型
delegate_->callNativeModules(
*this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
}
-
注:
构建底层Bridge的流程相当长,出现了一堆类,目的是构建出一个分工相对明确,结构相对清晰的运行环境,用于Native&JS通信、执行js bundle包。后序文章分析完Native&JS流程、js bundle包执行流程,可以回过头看启动原理,应该会对这个类体系有更清晰的理解。
5.加载js bundle包
加载js bundle包只分析Release模式下本地bundle整包加载流程,对于热更新/RAM拆包加载不做分析。这个过程相对简单,一句话概括:异步加载沙盒中的js bundle包。
// RCTCxxBridge.mm
- (void)start
{
...
// 异步加载 js bundle包
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self loadSource:^(NSError *error, RCTSource *source) {
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
} onProgress:^(RCTLoadingProgress *progressData) { ... }];
}
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
{
...
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, RCTSource *source) {
...
_onSourceLoad(error, source);
};
[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onProgress:onProgress onComplete:^(NSError *error, RCTSource *source) {
onSourceLoad(error, source);
}];
}
// RCTJavaScriptLoader.mm
+ (void)loadBundleAtURL:(NSURL *)scriptURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)onComplete
{
...
if (isCannotLoadSyncError) {
// 整包,异步加载
attemptAsynchronousLoadOfBundleAtURL(scriptURL, onProgress, onComplete);
} else { ... }
}
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;
}
...
}
6.执行js bundle包
上述所有工作完毕,意味着Native&JS通信、执行js bundle包的环境已经构建完毕,最后一个环节:执行执行js bundle包。执行流程图如下,其实就是把js bundle包经过层层传递,最终交给JavaScriptCore去执行。
js bundle执行流程图.png// 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];
}
});
}
- (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]];
}];
});
};
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
}
- (void)enqueueApplicationScript:(NSData *)script
url:(NSURL *)url
onComplete:(dispatch_block_t)onComplete
{
// 底层转化为在JS线程执行JS bundle
[self executeApplicationScript:script url:url async:YES];
// JS线程执行回调(由于底层js bundle最终会在JS线程执行,因此可以保证先执行完JS bundle,后执行成功回调)
if (onComplete) {
_jsMessageThread->runOnQueue(onComplete);
}
}
- (void)executeApplicationScript:(NSData *)script
url:(NSURL *)url
async:(BOOL)async
{
[self _tryAndHandleError:^{
NSString *sourceUrlStr = deriveSourceURL(url);
...
// 整包,异步执行
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));
});
}
// JSIExecutor.cpp
void JSIExecutor::loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) {
// 向JS环境全局对象global注入各种代理,用于向JS端传递原生模块信息、Native&JS互调
runtime_->global().setProperty( ... );
....
// 执行JS脚本
runtime_->evaluateJavaScript(std::make_unique<BigStringBuffer>(std::move(script)), sourceURL);
// 执行JS异步任务,JS call Navite
flush();
}
至此ReactNative启动流程已经完毕,开始运行js bundle代码,执行前面RCTRootView调用JS模块AppRegistry runApplication,运行组件,开始页面渲染。。。。
四、启动流程线程管理
RCTCxxBridge.mm
- (void)start
{
dispatch_group_t prepareBridge = dispatch_group_create();
// 3.加载自动注册的原生模块
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 4.js线程中初始化底层底层Bridge
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 5.加载 js bundle包(整包,异步加载)
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self loadSource:^(NSError *error, RCTSource *source) {
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
} onProgress:^(RCTLoadingProgress *progressData) { ... }];
// 6.执行js bundle 源码
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)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.requiresMainQueueSetup) {
dispatch_block_t block = ^{
// 创建模块实例
(void)[moduleData instance];
[moduleData gatherConstants];
};
if (initializeImmediately && RCTIsMainQueue()) {
block();
} else {
// 异步派发模块实例创建任务到主队列
if (dispatchGroup) {
dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
}
}
_modulesInitializedOnMainQueue++;
}
}
}
- 启动流程最后一个步骤“执行js bundle 源码”,需要等前面所有工作准备就绪才可以开始。ReactNative使用了GCD的队列组
dispatch_group_t
来实现:下列步骤用一个队列组来管理,- 3.创建需要在主线程创建的原生模块实例;
- 4.js线程中初始化底层底层Bridge;
- 5.加载 js bundle包(整包,异步加载)
等任务执行完毕通过dispatch_group_notify
通知执行6.执行js bundle包
。简单的线程管理使得3和4、5并发执行,并保证执行JS代码时,底层通信Bridge和原生模块都创建完毕。