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去執行。
// 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和原生模塊都創建完畢。