致敬
開始準(zhǔn)備寫這一篇文章的時(shí)候,中國(guó)的新型冠狀病毒肺炎疫情還在繼續(xù)夏醉,累積確診81058人,現(xiàn)存確診10827人涌韩。這篇文章寫完的時(shí)候畔柔,累計(jì)確診81501人,現(xiàn)存確認(rèn)5846人臣樱。疫情已經(jīng)持續(xù)了3個(gè)月靶擦,但也終將過(guò)去。毫無(wú)疑問(wèn)雇毫,在這漫長(zhǎng)的3個(gè)月的時(shí)間里玄捕,很多與疫情抗?fàn)幍墓ぷ魅藛T都是非常辛苦的,再次感謝&致敬棚放。
前言
這是一篇原理性文章枚粘,也是一篇源碼分析文章。這篇文章是筆者學(xué)習(xí)RN源碼過(guò)程中的一篇記錄文章飘蚯,主要記錄了程序從啟動(dòng)之初到開始執(zhí)行JS源碼的整個(gè)流程馍迄。從AppDelegate的application:didFinishLaunchingWithOptions:說(shuō)起福也,全流程涉及到關(guān)鍵類的初始化工作和JavaScript的執(zhí)行以及JS&Native之間的通信。圍繞bridge的初始化攀圈、JS源碼的加載拟杉、JS源碼的執(zhí)行、Native調(diào)用JS量承、JS調(diào)用Native展開分析搬设。內(nèi)容雖然很長(zhǎng),但其實(shí)很淺撕捍,大部分都是源碼拿穴,并沒(méi)有加入自己太多的思考,耐心看完就可以理解忧风。
本文篇幅很長(zhǎng)的原因是筆者貼出了大量的RN源碼默色。文章中的源碼已經(jīng)做了精簡(jiǎn),如果想看完整的代碼還是建議參考RN源碼狮腿。筆者主要?jiǎng)h除了源碼中與邏輯無(wú)強(qiáng)關(guān)聯(lián)的代碼腿宰。比如debug環(huán)境的宏、鎖缘厢、調(diào)試相關(guān)的代碼吃度、健壯性相關(guān)的代碼、錯(cuò)誤處理相關(guān)的代碼贴硫、代碼執(zhí)行耗時(shí)相關(guān)的代碼椿每。刪除這些代碼不會(huì)影響對(duì)源碼的閱讀和理解,請(qǐng)大家放心英遭。
閱讀這篇文章你最好具備以下條件:你應(yīng)該是一個(gè)iOS開發(fā)者间护,本文是站在一個(gè)iOS工程的角度分析RN的源碼,當(dāng)然如果你能看懂Objective-C代碼也是可以的挖诸。你應(yīng)該對(duì)RN有所了解汁尺,最好是使用RN開發(fā)過(guò)一些需求。你應(yīng)該對(duì)JS有所了解多律,本文會(huì)涉及少量JS代碼痴突。最后,你最好具備一些C++的知識(shí)菱涤,RN源碼中存在大量的C++代碼苞也,不需要會(huì)寫,了解C++語(yǔ)法能看懂C++代碼即可粘秆。當(dāng)然,如果你認(rèn)為萬(wàn)物皆對(duì)象收毫,以上條件都可以忽略攻走,那么讓我們開始吧》》》
本文基于React Native 0.61.5進(jìn)行分析殷勘。
名詞
本文中主要涉及到以下幾個(gè)類:RCTBridge、RCTCxxBridge昔搂、Instance玲销、NativeToJsBridge、JsToNativeBridge摘符、JSIExecutor贤斜、RCTRootView。他們的關(guān)系大概如下(JSIExecutor是本文涉及到的最內(nèi)層的類):
開始
我們新建一個(gè)名為NewProject的RN的iOS工程逛裤〈袢蓿可以看出AppDelegate.m的application: didFinishLaunchingWithOptions:方法實(shí)現(xiàn)是這樣的:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 1.初始化bridge
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// 2.初始化rootView
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"NewProject"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
// 3.設(shè)置rootViewController
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
以上代碼可以看出,application:didFinishLaunchWithOptions:主要做了3件事:
1.初始化一個(gè)RCTBridge實(shí)例
2.再用RCTBridge實(shí)例初始化一個(gè)rootView
3.用rootView配置一個(gè)rootViewController
第三步用rootView初始化一個(gè)rootViewController沒(méi)什么可說(shuō)的带族,本片文章我們主要窺探初始化RCTBridge和RCTRootView锁荔。
RCTBridge初始化
RCTBridge初始化是重點(diǎn)也是難點(diǎn),雖然叫RCTBridge的初始化蝙砌,但實(shí)際上不僅僅是初始化一個(gè)RCTBridge實(shí)例那么簡(jiǎn)單阳堕,在其背后還有RCTCxxBridge、NativeToJSBridge择克、JSExecutor(JSIExecutor生產(chǎn)環(huán)境/RCTObjcExecutor調(diào)試環(huán)境)恬总、JsToNativeBridge的初始化,這里僅作為一個(gè)了解肚邢,不必糾結(jié)越驻,后面會(huì)詳細(xì)介紹。先來(lái)看一下appDelegate中調(diào)用的RCTBridge的初始化的源碼實(shí)現(xiàn):
// 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];
}
return self;
}
- (void)setUp
{
// 獲取bridgeClass 默認(rèn)是RCTCxxBridge
Class bridgeClass = self.bridgeClass;
// 只有delegate返回的bundleURL發(fā)生變化才更新_bundleURL
NSURL *previousDelegateURL = _delegateBundleURL;
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
_bundleURL = _delegateBundleURL;
}
// 初始化self.batchedBridge道偷,也就是RCTCxxBridge
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
// 啟動(dòng)RCTCxxBridge
[self.batchedBridge start];
}
// self.bridgeClass
- (Class)bridgeClass
{
return [RCTCxxBridge class];
}
上面RCTBridge的初始化方法是創(chuàng)建了一個(gè)RCTBridge實(shí)例缀旁,通過(guò)調(diào)用私有方法setUp對(duì)bridge進(jìn)行配置。setUp主要做了2件事情:
1.初始化self.batchedBridge勺鸦,也就是RCTCxxBridge實(shí)例
2.啟動(dòng)self.bathedBridge(RCTCxxBridge實(shí)例)
self.batchedBridge的start源碼如下:
// RCTCxxBridge.mm
- (void)start {
// 1.提前設(shè)置并開啟JS線程 _jsThread
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
_jsThread.name = RCTJSThreadName; // @"com.facebook.react.JavaScript"
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
[_jsThread start];
dispatch_group_t prepareBridge = dispatch_group_create();
// 2.初始化注冊(cè)native module
[self registerExtraModules];
// 初始化所有不能被懶加載的native module
[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules];
_reactInstance.reset(new Instance);
__weak RCTCxxBridge *weakSelf = self;
// 準(zhǔn)備executor factory
std::shared_ptr<JSExecutorFactory> executorFactory;
if (!self.executorClass) {
if ([self.delegate conformsToProtocol:@ (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];
}
}));
}
// 3.module初始化完就初始化底層Instance實(shí)例并巍,也就是_reactInstance
// 在JS線程初始化_reactInstance、RCTMessageThread换途、nativeToJsBridge懊渡、JSCExecutor
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 4.異步加載JS代碼
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self loadSource:^(NSError *error, RCTSource *source) {
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
}];
// 5.等待native moudle 和 JS 代碼加載完畢后就執(zhí)行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];
}
});
}
這個(gè)start方法很關(guān)鍵,是RCTCxxBridge中最主要的方法军拟。因?yàn)樽龅氖虑檩^多且摻雜著部分C++代碼剃执, 所以讓人覺(jué)得很復(fù)雜,梳理一下懈息,其實(shí)start中主要做了5件事:
1.創(chuàng)建并開啟一個(gè)JS線程(_jsThread)肾档,該線程綁定了一個(gè)runloop,顧名思義,這個(gè)線程就是用來(lái)執(zhí)行JS代碼的怒见,后續(xù)所有的js代碼都是在這個(gè)線程里執(zhí)行俗慈。
2.初始化注冊(cè)所有要暴露給js調(diào)用的native module,每個(gè)native module類都封裝成一個(gè)RCTModuleData實(shí)例遣耍,如果需要在主線程中創(chuàng)建某些類的實(shí)例闺阱,則會(huì)在主線程中去創(chuàng)建實(shí)例。這些RCTModuleData會(huì)分別存儲(chǔ)在字典和數(shù)組里舵变。
3.準(zhǔn)備JS和Native之間的橋和JS運(yùn)行環(huán)境酣溃,初始化JSExecutorFactory實(shí)例(顧名思義,JSExecutorFactory是一個(gè)JSExecutor的工廠纪隙,也就是負(fù)責(zé)生產(chǎn)JSExecutor實(shí)例的工廠)赊豌,然后在JS線程中創(chuàng)建JS的RCTMessageThread,初始化_reactInstance(Instance實(shí)例)瘫拣、nativeToJsBridge(NativeToJsBridge實(shí)例)亿絮、executor(JSIExecutor實(shí)例)。以上這些事情主要是在_initializeBridge:方法中完成的麸拄,此處作為了解派昧,后面詳細(xì)分析。
4.異步加載JS源碼
5.native module和JS代碼都加載完畢后就執(zhí)行JS代碼
以上提到了三個(gè)C++類Instance、NativeToJsBridge、JSIExecutor何恶,他們都是native call JS的橋梁郊霎。但有什么區(qū)別呢昏名?其實(shí)他們的關(guān)系是Instance->NativeToJsBridge->JSIExecutor。即Instance中創(chuàng)建并持有NativeToJsBridge實(shí)例,NativeToJsBridge中又創(chuàng)建并持有JSIExecutor實(shí)例。換句話說(shuō)泻拦,Instance是對(duì)NativeToJsBridge的封裝,NativeToJsBridge是對(duì)JSIExecutor的封裝忽媒。
上述源碼里用到一個(gè)叫prepareBridge的dispatch_group_t争拐,雖然名稱叫prepareBridge,但其實(shí)是一個(gè)dispatch_group_t晦雨。dispatch_group_t和dispatch_group_notify聯(lián)合使用保證異步代碼同步按順序執(zhí)行架曹,也就是被添加到group中的任務(wù)都做完了之后再執(zhí)行notify中的任務(wù)(但group中的多個(gè)任務(wù)的執(zhí)行順序是無(wú)序的)。有很多初始化工作是異步并行的闹瞧,運(yùn)行JS源碼是在所有準(zhǔn)備工作之后才能進(jìn)行绑雄,所以用了dispatch_group_t和dispatch_group_notify機(jī)制來(lái)確保這個(gè)問(wèn)題。
在上述源碼里我們看到了一個(gè)名為ensureOnJavaScriptThread:的方法奥邮,如下:
// RCTCxxBridge.mm
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
看名字就知道万牺,ensureOnJavaScriptThread:是RCTCxxBridge里專門將block放在JS線程中執(zhí)行的方法罗珍。他的目的就是確保待執(zhí)行的block能在JS線程執(zhí)行,所以他接收一個(gè)block作為參數(shù)杏愤,然后判斷當(dāng)前線程是否是先前創(chuàng)建的JS線程靡砌,如果是則立即在JS線程同步執(zhí)行block已脓,否則切換到JS線程執(zhí)行block珊楼。源碼如下:
// 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];
}
}
大家常說(shuō)JS是單線程的,在RN里就是這樣的度液。native側(cè)創(chuàng)建了一個(gè)專門服務(wù)于JS的線程厕宗,然后綁定了一個(gè)runloop不讓這個(gè)JS線程退出,后續(xù)JS代碼都是在這個(gè)線程里執(zhí)行堕担。
剛才上面只是穿插介紹了ensureOnJavaScriptThread的作用已慢。回過(guò)頭來(lái)看start方法中ensureOnJavaScriptThread:的block主要是在JS線程執(zhí)行了weakSelf _initializeBridge:executorFactory; 接下來(lái)看下_initializeBridge:到底做了哪些事情霹购,源碼如下:
// RCTCxxBridge.mm
- (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
RCTAssertJSThread();
__weak RCTCxxBridge *weakSelf = self;
_jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
});
if (_reactInstance) {
[self _initializeBridgeLocked:executorFactory];
}
}
- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
// This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
_reactInstance->initializeBridge(
std::make_unique<RCTInstanceCallback>(self),
executorFactory,
_jsMessageThread,
[self _buildModuleRegistryUnlocked]);
_moduleRegistryCreated = YES;
}
如上佑惠,通過(guò)assert不難看出,RCTCxxBridge的 _initializeBridge:方法確實(shí)是在JSThread中調(diào)用的齐疙。且 _initializeBridge:方法主要做了2件事:
1.創(chuàng)建一個(gè)名為_jsMessageThread的RCTMessageThread實(shí)例膜楷,并被RCTCxxBridge持有(可以看出messageThread實(shí)際上是由runloop實(shí)現(xiàn)的)
2.調(diào)用_initializeBridgeLocked:傳入RCTExecutorFactory初始化bridge(nativeToJsBridge實(shí)例)
_initializeBridgeLocked:的實(shí)現(xiàn)更簡(jiǎn)單,_initializeBridgeLocked:內(nèi)部調(diào)用了_reactInstance的initializeBridge方法繼續(xù)初始化bridge(NativeToJsBrige實(shí)例)贞奋。如注釋所述赌厅,這個(gè)方法是異步調(diào)用的,但是所有經(jīng)由_reactInstance實(shí)例對(duì)JS方法的調(diào)用都會(huì)被Instance中名為m_syncReady這個(gè)成員變量給鎖住轿塔。
此處先忽略上面的第四個(gè)參數(shù)self _buildModuleRegistryUnlocked 特愿,繼續(xù)看_reactInstance的initializeBridge方法:
// 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_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
}
不難看出,在jsQueue(MessageQueueThread)線程里最主要?jiǎng)?chuàng)建了NativeToJsBrige實(shí)例勾缭,即nativeToJsBridge_揍障。然后instance持有了這個(gè)nativeToJsBridge_以及其他2個(gè)外部傳進(jìn)來(lái)的參數(shù) callback、moduleRegistry俩由。至此Instance的初始化就結(jié)束了毒嫡。下面我們來(lái)看一下這四個(gè)參數(shù):
第一個(gè)參數(shù)是InstanceCallback類型的回調(diào),用于底層執(zhí)行結(jié)束后往上層回調(diào)采驻,實(shí)際上調(diào)用方傳遞的是self审胚,即RCTCxxBridge實(shí)例。
第二個(gè)參數(shù)是JSExecutorFactory礼旅,即生產(chǎn)JSExecutor的工廠實(shí)例膳叨。Instance內(nèi)部會(huì)使用這個(gè)facotry獲得一個(gè)JSExecutor實(shí)例。
第三個(gè)參數(shù)就是我們?cè)谕饷鎰?chuàng)建的MessageQueueThread痘系。
第四個(gè)參數(shù)moduleRegistry是ModuleRegistry類型的實(shí)例菲嘴。moduleRegistry里面包含了所有的Native Module信息,即RCTModuleData。即將前面生成的所有RCTModuleData傳給了_reactInstance龄坪。至此我們知道self _buildModuleRegistryUnlocked實(shí)際上是返回了一個(gè)RCTModuleRegistry實(shí)例昭雌。
下面簡(jiǎn)單介紹后3個(gè)參數(shù):
JSExecutorFactory
JSExecutorFactory,顧名思義用于生產(chǎn)JSExecutor實(shí)例健田,JSExecutor用于執(zhí)行JS烛卧,也是JS和Native之間的橋梁。無(wú)論是Native call JS還是JS call Native妓局,JSExecutor都起到了至關(guān)重要的作用总放。生產(chǎn)環(huán)境下使用的是JSCExecutorFactory,返回JSIExecutor用于執(zhí)行JS好爬,開發(fā)環(huán)境使用的是RCTObjcExecutorFactory局雄,返回RCTObjcExecutor通過(guò)websocket鏈接chrome執(zhí)行JS。
// JSExecutor.h
class JSExecutorFactory {
public:
virtual std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) = 0;
virtual ~JSExecutorFactory() {}
};
MessageQueueThread
MessageQueueThread類型對(duì)象用于提供隊(duì)列執(zhí)行存炮。這里是由RCTMessageThread來(lái)實(shí)現(xiàn)炬搭,內(nèi)部用的是CFRunLoop來(lái)實(shí)現(xiàn)。
除RCTMessageThread之外穆桂,另一個(gè)實(shí)現(xiàn)是DispatchMessageQueueThread宫盔,我們不做詳細(xì)介紹。
// MessageQueueThread.h
namespace facebook {
namespace react {
class MessageQueueThread {
public:
virtual ~MessageQueueThread() {}
virtual void runOnQueue(std::function<void()>&&) = 0;
virtual void runOnQueueSync(std::function<void()>&&) = 0;
virtual void quitSynchronous() = 0;
};
}}
ModuleRegistry
上面說(shuō)了moduleRegistry中包括了所有native module信息充尉,即RCTModuleData飘言。這還要從我們剛才忽略的self _buildModuleRegistryUnlocked方法說(shuō)起,_buildModuleRegistryUnlocked方法主要負(fù)責(zé)構(gòu)建一個(gè)RCTModuleRegistry實(shí)例并返回驼侠,如下:
// RCTCxxBridge.mm
- (std::shared_ptr<ModuleRegistry>)_buildModuleRegistryUnlocked
{
__weak __typeof(self) weakSelf = self;
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.instance就是native module實(shí)例對(duì)象
[moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; },
std::make_shared<DispatchMessageQueueThread>(moduleData)));
} else {
nativeModules.emplace_back(std::make_unique<RCTNativeModule>(bridge, moduleData));
}
}
return nativeModules;
}
可以看出姿鸿,上面使用_moduleDataByID初始化并返回了一個(gè)ModuleRegistry實(shí)例。_moduleDataByID是一個(gè)含有若干個(gè)RCTModuleData對(duì)象的數(shù)組倒源。在https://cloud.tencent.com/developer/article/1597259中我們介紹了_moduleDataByID中的RCTModuleData實(shí)際上就是在程序啟動(dòng)后load各個(gè)class的時(shí)候被收集的苛预。有必要提一下,上面源碼中的moduleData.instance其實(shí)就是native module的實(shí)例對(duì)象笋熬。通過(guò)如下代碼可窺見一斑:
// RCTModuleData.mm
- (instancetype)initWithModuleClass:(Class)moduleClass
bridge:(RCTBridge *)bridge
{
return [self initWithModuleClass:moduleClass
moduleProvider:^id<RCTBridgeModule>{ return [moduleClass new]; }
bridge:bridge];
}
- (instancetype)initWithModuleClass:(Class)moduleClass
moduleProvider:(RCTBridgeModuleProvider)moduleProvider
bridge:(RCTBridge *)bridge
{
if (self = [super init]) {
_bridge = bridge;
_moduleClass = moduleClass;
_moduleProvider = [moduleProvider copy];
[self setUp];
}
return self;
}
- (instancetype)initWithModuleInstance:(id<RCTBridgeModule>)instance
bridge:(RCTBridge *)bridge
{
if (self = [super init]) {
_bridge = bridge;
_instance = instance;
_moduleClass = [instance class];
[self setUp];
}
return self;
}
至此热某,RN中Instance實(shí)例(_reactInstance)的初始化已經(jīng)介紹完了,接下來(lái)介紹NativeToJSBridge胳螟。為什么要介紹NativeToJsBridge昔馋?因?yàn)槲覀兩厦嬲f(shuō)了Instance是對(duì)NativeToJSBridge的封裝,就像UIView是對(duì)CALayer的封裝一樣糖耸,可見NativeToJSBridge比Instance更加接近底層(實(shí)際JSExecutor比NativeToJSBridge更加底層秘遏,我們后面詳細(xì)的說(shuō)明)。
NativeToJSBridge
NativeToJsBridge的主要作用是負(fù)責(zé)管理所有native對(duì)JS的調(diào)用嘉竟,并且也管理了executor們和他們的線程邦危。NativeToJsBridge的所有函數(shù)可以在任意線程被調(diào)用洋侨。除非某些方法是為了同步加載Application Script,否則所有的方法都是在JSQueue線程排隊(duì)等候執(zhí)行的倦蚪,且這些函數(shù)會(huì)被立即返回希坚。這也說(shuō)明大部分Native call JS的方法都是在jsQueue這個(gè)線程執(zhí)行的,而jsQueue實(shí)際上就是messageQueueThread陵且。
在上面Instance::initializeBridge函數(shù)中裁僧,我們知道Instance創(chuàng)建了一個(gè)名為nativeToJsBridge_的NativeToJSBridge的實(shí)例并被Instance實(shí)例持有。如下:
// 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_成員變量
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
});
}
可以看出nativeToJsBirdge的類型是NativeToJsBridge滩报。下面我們來(lái)看下NativeToJsBridge類的具體定義锅知。
NativeToJSBridge定義
// NativeToJsBridge.cpp
class NativeToJsBridge {
public:
friend class JsToNativeBridge;
// 必須在主線程調(diào)用
NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback);
virtual ~NativeToJsBridge();
// 傳入module ID播急、method ID脓钾、參數(shù)用于在JS側(cè)執(zhí)行一個(gè)函數(shù)
void callFunction(std::string&& module, std::string&& method, folly::dynamic&& args);
// 通過(guò)callbackId調(diào)用JS側(cè)的回調(diào)
void invokeCallback(double callbackId, folly::dynamic&& args);
// 開始執(zhí)行JS application. 如果bundleRegistry非空,就會(huì)使用RAM的方式 讀取JS源碼文件
// 否則就假定 startupCode 已經(jīng)包含了所有的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;
};
如上桩警,NativeToJsBridge是一個(gè)C++類可训,它主要有如下3個(gè)重要的成員變量:
-
<JsToNativeBridge> m_delegate
m_delegate是JsToNativeBridge類型的引用,主要用于JS call Native
-
<JSExecutor> m_executor
JSExecutor類型引用捶枢,主要用于執(zhí)行Native call JS握截,實(shí)際上生產(chǎn)環(huán)境使用的是JSIExecutor;調(diào)試環(huán)境使用的是RCTObjcExecutor
-
<MessageQueueThread> m_executorMessageQueueThread
MessageQueueThread類型引用烂叔,內(nèi)部是由runloop實(shí)現(xiàn)的谨胞,由外部傳遞,用于隊(duì)列管理蒜鸡。這里的外部傳遞是指m_executorMessageQueueThread并非NativeToJsBridge自己初始化的胯努,而是作為初始化NativeToJsBridge的參數(shù)由上層傳遞進(jìn)來(lái)的。如果你還記得NativeToJsBridge是在Instance::initializeBridge中初始化的逢防,那么你就知道這個(gè)m_executorMessageQueueThread最終起源于RCTCxxBridge中的_jsMessageThread叶沛,進(jìn)而由一步一步的函數(shù)調(diào)用,穿山越嶺傳遞進(jìn)來(lái)的忘朝。所以灰署,NativeToJsBridge的m_executorMessageQueueThread就是 了RCTCxxBridge的_jsMessageThread。
除了以上3個(gè)關(guān)鍵的屬性之外局嘁,NativeToJsBridge還定義了4個(gè)函數(shù):
-
void callFunction(std::string&& module, std::string&& method, folly::dynamic&& args);
這個(gè)函數(shù)的意義就是通過(guò)module ID和method ID以及參數(shù)去調(diào)用JS方法
void invokeCallback(double callbackId, folly::dynamic&& args);
這個(gè)函數(shù)的意義就是通過(guò)callbackId和參數(shù)觸發(fā)一個(gè)JS的回調(diào)溉箕。通常是JS call Native method之后,native把一些異步的執(zhí)行結(jié)果再以callback的形式回調(diào)給JS悦昵。
-
void loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);這個(gè)方法的作用是執(zhí)行JS代碼肴茄,他還有一個(gè)兄弟叫做loadApplicationSync,顧名思義旱捧,他兄弟是一個(gè)同步函數(shù)独郎,所以他自己就是異步執(zhí)行JS代碼踩麦。
NativeToJsBridge構(gòu)造函數(shù)
上面說(shuō)了NativeBridge的3個(gè)關(guān)鍵屬性和4個(gè)關(guān)鍵方法,接下來(lái)我們說(shuō)下他的構(gòu)造函數(shù)氓癌,接觸過(guò)C++的開發(fā)者應(yīng)該知道谓谦,C++中類的構(gòu)造函數(shù)和類同名,如下是NativeToJsBridge的構(gòu)造函數(shù):
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()) {}
上面我們?cè)贗nstance::initializeBridge中就已經(jīng)調(diào)用過(guò)NativeToJsBridge的構(gòu)造函數(shù)創(chuàng)建了一個(gè)NativeToJsBridge實(shí)例并被賦值給Instance實(shí)例的成員變量nativeToJsBridge_贪婉。在NativeToJSBridge構(gòu)造函數(shù)的后面有一個(gè)初始化列表反粥,其中registry和callback作為入?yún)⑸闪艘粋€(gè)JsToNativeBridge類型實(shí)例賦值給m_delegate。jsExecutorFactory又通過(guò)m_delegate和jsQueue生產(chǎn)了一個(gè)executor賦值給m_executor(m_delegate最終是給m_executor使用的疲迂,在生產(chǎn)環(huán)境下才顿,jsQueue對(duì)于executor也是無(wú)用的),m_executorMessageQueueThread最后指向了jsQueue尤蒿。
JSIExecutor
和Instance郑气、NativeToJsBridge一樣,JSIExecutor主要用來(lái)Native call JS腰池,但他是比Instance和NativeToJsBridge更深層次的一個(gè)核心類尾组,換句話說(shuō),我們可以把NativeToJsBridge理解為JSIExecutor的包裝(而Instance又是對(duì)NativeToJsBridge的包裝)示弓,對(duì)Instance的調(diào)用最終都會(huì)走到NativeToJsBridge讳侨,對(duì)NativeToJsBridge的調(diào)用最終都會(huì)走到JSIExecutor,比如getJavaScriptContext奏属、callFunction跨跨、invokeCallback這些方法。他們的調(diào)用順序是Instance->NativeToJsBridge->JSIExecutor囱皿。上面我們說(shuō)了勇婴,在NativeToJsBridge的構(gòu)造函數(shù)中jsExecutorFactory使用JsToNativeBridge實(shí)例m_delegate和jsQueue創(chuàng)建了m_executor(實(shí)際上生產(chǎn)環(huán)境下只用了m_delegate)。這里我們主要以生產(chǎn)環(huán)境的JSIExecutor為例介紹铆帽。調(diào)試模式下請(qǐng)參考RCTObjcExecutor咆耿,他們都繼承自JSExecutor。下面是兩種環(huán)境下executor的創(chuàng)建方式爹橱,生產(chǎn)環(huán)境的JSIExecutor通過(guò)JSCExecutorFactory生產(chǎn)萨螺,調(diào)試模式下的RCTObjcExecutor通過(guò)RCTObjcExecutorFactory生產(chǎn),如下:
// JSCExecutorFactory.mm
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> __unused jsQueue) {
return folly::make_unique<JSIExecutor>(
facebook::jsc::makeJSCRuntime(),
delegate,
JSIExecutor::defaultTimeoutInvoker,
std::move(installBindings));
}
// RCTObjcExecutor.mm
std::unique_ptr<JSExecutor> RCTObjcExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) {
return std::unique_ptr<JSExecutor>(
new RCTObjcExecutor(m_jse, m_errorBlock, jsQueue, delegate));
}
JSIExecutor主要的幾個(gè)關(guān)鍵屬性:
- <jsi::Runtime> runtime_
Runtime類型指針愧驱,代表JS的運(yùn)行時(shí)慰技。這是一個(gè)抽象類,其實(shí)際上是由JSCRuntime來(lái)實(shí)現(xiàn)的组砚。JSCRuntime實(shí)現(xiàn)了<jsi::Runtime>接口吻商,提供了創(chuàng)建JS上下文的功能,同時(shí)可以執(zhí)行JS糟红。如下是JSCRuntime的evaluateJavaScript方法實(shí)現(xiàn):
// JSCRuntime.cpp
jsi::Value JSCRuntime::evaluateJavaScript(
const std::shared_ptr<const jsi::Buffer> &buffer,
const std::string& sourceURL) {
std::string tmp(
reinterpret_cast<const char*>(buffer->data()), buffer->size());
JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str());
JSStringRef sourceURLRef = nullptr;
if (!sourceURL.empty()) {
sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str());
}
JSValueRef exc = nullptr;
JSValueRef res = JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc);
return createValue(res);
}
- <ExecutorDelegate> delegate_
ExecutorDelegate類型的指針艾帐,這里的ExecutorDelegate是抽象類乌叶,實(shí)際是由JsToNativeBridge實(shí)現(xiàn)的。也即JSIExecutor引用了JsToNativeBridge實(shí)例柒爸。還記得NativeToJsBridge中的JsToNativeBridge類型的成員變量m_delegate嗎准浴?其實(shí)這里的delegate_就是NativeToJsBridge中的m_delegate。
- <JSINativeModules> nativeModules_
JSINativeModules由上層傳入的ModuleRegistry構(gòu)造而成捎稚,同時(shí)會(huì)將ModuleRegistry中包含的本地模塊配置信息通過(guò)”__fbGenNativeModule”保存到JS端乐横。
JSINativeModules有個(gè)getModule方法,getModule方法內(nèi)又調(diào)用了 createModule方法今野,createModule方法生成了module信息葡公,源碼如下:
// JSINativeModules.cpp
Value JSINativeModules::getModule(Runtime& rt, const PropNameID& name) {
std::string moduleName = name.utf8(rt);
// 調(diào)用createModule方法
auto module = createModule(rt, moduleName);
auto result =
m_objects.emplace(std::move(moduleName), std::move(*module)).first;
return Value(rt, result->second);
}
folly::Optional<Object> JSINativeModules::createModule(
Runtime& rt,
const std::string& name) {
if (!m_genNativeModuleJS) {
// runtime獲取名為__fbGenNativeModule的函數(shù)指針賦值給m_genNativeModuleJS
// JS端的函數(shù)__fbGenNativeModule調(diào)用最終就會(huì)走到這里。
m_genNativeModuleJS =
rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
}
auto result = m_moduleRegistry->getConfig(name);
// 調(diào)用m_genNativeModuleJS函數(shù)条霜,即__fbGenNativeModule
Value moduleInfo = m_genNativeModuleJS->call(
rt,
valueFromDynamic(rt, result->config),
static_cast<double>(result->index));
folly::Optional<Object> module(
moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));
// 返回生成的module
return module;
}
那到這里你會(huì)問(wèn)JSINativeModules負(fù)責(zé)createModule并提供了可以訪問(wèn)某個(gè)module的接口getModule催什。那是誰(shuí)調(diào)用的JSINativeModules的getModule呢?全局搜索getModule不難發(fā)現(xiàn)蛔外,getModule是在一個(gè)名為NativeModuleProxy的get方法里調(diào)用的蛆楞,如下:
// JSIExecutor.cpp
class JSIExecutor::NativeModuleProxy : public jsi::HostObject {
public:
// 構(gòu)造函數(shù) JSIExecutor實(shí)例作為NativeModuleProxy構(gòu)造函數(shù)的入?yún)? NativeModuleProxy(JSIExecutor &executor) : executor_(executor) {}
// NativeModuleProxy 的 get方法 用于獲取native module信息
Value get(Runtime &rt, const PropNameID &name) override {
return executor_.nativeModules_.getModule(rt, name);
}
};
那么NativeModuleProxy這個(gè)C++類又是在哪里使用的呢?全局搜索NativeModuleProxy夹厌,你會(huì)發(fā)現(xiàn)只有一個(gè)地方再使用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_->global().setProperty( //... ); runtime是一個(gè)JSCRuntime類型對(duì)象,通過(guò)調(diào)用rumtime_->global()獲得一個(gè)全局的global對(duì)象光稼。然后又通過(guò)setProperty方法給global對(duì)象設(shè)置了一個(gè)名為nativeModuleProxy的對(duì)象或南。日后(JS側(cè)的)global對(duì)象通過(guò)"nativeModuleProxy"這個(gè)名字即可訪問(wèn)到(native側(cè)的)NativeModuleProxy,這聽起來(lái)像是一句廢話艾君。說(shuō)到這里采够,我們不得不說(shuō)一下JS側(cè)的global.nativeModuleProxy,我們會(huì)詫異于在native側(cè)和JS側(cè)的global中都存在nativeModuleProxy變量冰垄,其實(shí)這不是巧合蹬癌,本質(zhì)上,JS側(cè)的global.nativeModuleProxy就是native側(cè)的nativeModuleProxy虹茶。換句話說(shuō)逝薪,我們?cè)贘S側(cè)的NativeModules對(duì)應(yīng)的就是native側(cè)的nativeModuleProxy。JS側(cè)代碼如下:
// 源碼位置:react-native/Libraries/BatchedBridge/NativeModules.js
let NativeModules: {[moduleName: string]: Object} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else if (!global.nativeExtensions) {
const bridgeConfig = global.__fbBatchedBri
通過(guò)上述JS代碼可以看出蝴罪,JS側(cè)的NativeModules == JS側(cè)的global.nativeModuleProxy == native側(cè)NativeModuleProxy董济。
JS側(cè)對(duì)NativeModules的調(diào)用都會(huì)經(jīng)由native側(cè)的NativeModuleProxy后進(jìn)而調(diào)用到JSINativeModules的的createModule這個(gè)實(shí)例方法返回了moduleName對(duì)應(yīng)的module config。值得一提的是要门,在createModule方法中虏肾,還調(diào)用了一個(gè)名為“__fbGenNativeModule”的JS方法廓啊,如下:
// JSINativeModules.cpp
// JSINativeModules::createModule方法中
if (!m_genNativeModuleJS) {
m_genNativeModuleJS =
rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
}
Value moduleInfo = m_genNativeModuleJS->call(
rt,
valueFromDynamic(rt, result->config),
static_cast<double>(result->index));
通過(guò)runtime獲取JS全局對(duì)象global,接著通過(guò)方法名“__fbGenNativeModule”從golbal實(shí)例中獲取這個(gè)JS方法指針封豪,然后進(jìn)行調(diào)用崖瞭。當(dāng)然,要想在native側(cè)可以調(diào)用到這個(gè)JS方法撑毛,前提是需要在JS側(cè)對(duì)這個(gè)方法進(jìn)行定義书聚。如下是這個(gè)方法在JS側(cè)的源碼實(shí)現(xiàn):
// 源碼位置: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 = {};
methods &&
methods.forEach((methodName, methodID) => {
const isPromise =
promiseMethods && arrayContains(promiseMethods, methodID);
const isSync = syncMethods && arrayContains(syncMethods, methodID);
const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
module[methodName] = genMethod(moduleID, methodID, methodType);
});
return {name: moduleName, module};
}
// 導(dǎo)出genModule到全局變量global上以便native可以調(diào)用
global.__fbGenNativeModule = genModule;
上面JS源碼先是定義了一個(gè)名為genModule的函數(shù),然后又把這個(gè)函數(shù)掛載到了全局變量global的"__fbGenNativeModule"上藻雌,以便作為一個(gè)可以全局訪問(wèn)的全局函數(shù)雌续,這樣做的目的是方便在native側(cè)調(diào)用。所以胯杭,__fbGenNativeModule這個(gè)函數(shù)指代的就是gemModule驯杜。
到這里JSIExecutor的初始化完成了,這樣和JS之間的橋梁就建好了做个,以后Native call JS都會(huì)先后經(jīng)由Instance鸽心、NativeToJSBridge、JSIExecutor最終到達(dá)JS居暖。
下圖描述了bridge初始化的方法調(diào)用時(shí)序圖和涉及到的主要類:
加載JS代碼
然后顽频,我們不要忘了,以上這一大段篇幅只是初始化了RCTCxxBridge中的_reactInstance以及instance背后的NativeToJsBridge太闺、JSIExecutor糯景。我們還記得RCTCxxBridge的start方法中,除了初始化_reactInstance省骂、NativeToJSBridge蟀淮、JSIExecutor之外,與之同時(shí)進(jìn)行的還有加載JS源碼钞澳,jsBundle的加載是通過(guò)RCTJavaScriptLoader進(jìn)行的怠惶。當(dāng)初始化工作和JS源碼加載都完成后,就會(huì)執(zhí)行JS源碼轧粟。讓我們來(lái)回顧一下RCTCxxBridge.mm中的start方法:
// RCTCxxBridge.mm
- (void)start {
// 此處省略若干行...
// 1. 初始化native module
dispatch_group_t prepareBridge = dispatch_group_create();
// 2. 在JS線程初始化_reactInstance策治、RCTMessageThread、nativeToJsBridge逃延、JSCExecutor
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 3. 異步加載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) {
// 展示加載bundle 的 loadingView
#if RCT_DEV && __has_include(<React/RCTDevLoadingView.h>)
RCTDevLoadingView *loadingView = [weakSelf moduleForName:RCTBridgeModuleNameForClass([RCTDevLoadingView class])
lazilyLoadIfNecessary:NO];
[loadingView updateProgress:progressData];
#endif
}];
// 4. 等待native moudle 和 JS 代碼加載完畢后就執(zhí)行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];
}
});
}
很明顯览妖,在初始化_reactInstance完成之后,還有異步load JS源碼以及執(zhí)行源碼的工作(實(shí)際上揽祥,因?yàn)閐ispatch_group_t的原因讽膏,初始化_reactInstance和load JS源碼是并發(fā)執(zhí)行的,但只有在兩者工作都完畢后才去執(zhí)行JS代碼)拄丰。本節(jié)我們將會(huì)介紹JS代碼的加載府树。
// RCTCxxBridge.mm
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
{
// 發(fā)送通知 將要加載JS代碼
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center postNotificationName:RCTBridgeWillDownloadScriptNotification object:_parentBridge];
// JS代碼加載完成的回調(diào)
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, RCTSource *source) {
NSDictionary *userInfo = @{
RCTBridgeDidDownloadScriptNotificationSourceKey: source ?: [NSNull null],
RCTBridgeDidDownloadScriptNotificationBridgeDescriptionKey: self->_bridgeDescription ?: [NSNull null],
};
[center postNotificationName:RCTBridgeDidDownloadScriptNotification object:self->_parentBridge userInfo:userInfo];
_onSourceLoad(error, source);
};
// 通知delegate
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:onProgress:onComplete:)]) {
[self.delegate loadSourceForBridge:_parentBridge onProgress:onProgress onComplete:onSourceLoad];
} else if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
[self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad];
} else {
// 加載JS代碼
__weak RCTCxxBridge *weakSelf = self;
[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onProgress:onProgress onComplete:^(NSError *error, RCTSource *source) {
onSourceLoad(error, source);
}];
}
}
可以看出俐末,上述代碼中通過(guò)調(diào)用RCTJavaScriptLoader的類方法loadBundleAtURL:onProgress:onComplete加載JS bundle。如下是源碼:
// RCTJavaScriptLoader.mm
+ (void)loadBundleAtURL:(NSURL *)scriptURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)onComplete
{
int64_t sourceLength;
NSError *error;
// 嘗試 同步加載JS bundle
NSData *data = [self attemptSynchronousLoadOfBundleAtURL:scriptURL
runtimeBCVersion:JSNoBytecodeFileFormatVersion
sourceLength:&sourceLength
error:&error];
if (data) {
onComplete(nil, RCTSourceCreate(scriptURL, data, sourceLength));
return;
}
const BOOL isCannotLoadSyncError =
[error.domain isEqualToString:RCTJavaScriptLoaderErrorDomain]
&& error.code == RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously;
// 嘗試 異步加載JS bundle
if (isCannotLoadSyncError) {
attemptAsynchronousLoadOfBundleAtURL(scriptURL, onProgress, onComplete);
} else {
onComplete(error, nil);
}
}
不難看出奄侠,上面一段代碼還是很清晰的卓箫,主要做了2件事:
1.嘗試同步加載JS bundle,加載成功就執(zhí)行onComplete回調(diào)
2.如果不能同步加載則嘗試異步加載JS bundle垄潮,否則直接onComplete
那么什么情況下可以同步加載JS bundle烹卒?答案是如果要加載的bundle是本地預(yù)置的或是已經(jīng)下載好的,那么就可以同步加載弯洗,否則只能異步download旅急。
下面我們分別來(lái)看下同步加載bundle和異步加載bundle的實(shí)現(xiàn)。
同步加載bundle
// RCTJavaScriptLoader.mm
+ (NSData *)attemptSynchronousLoadOfBundleAtURL:(NSURL *)scriptURL
runtimeBCVersion:(int32_t)runtimeBCVersion
sourceLength:(int64_t *)sourceLength
error:(NSError **)error
{
// 如果bundle不在本地牡整,那么就不能同步加載
if (!scriptURL.fileURL) {
if (error) {
*error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain
code:RCTJavaScriptLoaderErrorCannotBeLoadedSynchronously
userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"Cannot load %@ URLs synchronously",
scriptURL.scheme]}];
}
return nil;
}
// 檢查前4個(gè)字節(jié)來(lái)判斷這個(gè)bundle是普通bundle還是RAM bundle
// 如果是RAM bundle藐吮,則在前4個(gè)字節(jié)有一個(gè)數(shù)字 `(0xFB0BD1E5)`
// RAM bundle相對(duì)于普通bundle的好處是當(dāng)有需要時(shí)再去以“懶加載”的形式2把modules注入到JSC
FILE *bundle = fopen(scriptURL.path.UTF8String, "r");
if (!bundle) {
if (error) {
*error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain
code:RCTJavaScriptLoaderErrorFailedOpeningFile
userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"Error opening bundle %@", scriptURL.path]}];
}
return nil;
}
// 讀取header
// 關(guān)于fread各個(gè)參數(shù)的解釋:
// __ptr -- 這是指向帶有最小尺寸 size*nitems 字節(jié)的內(nèi)存塊的指針。
// __size -- 這是要讀取的每個(gè)元素的大小逃贝,以字節(jié)為單位谣辞。
// __nitems -- 這是元素的個(gè)數(shù),每個(gè)元素的大小為 size 字節(jié)沐扳。
// __stream -- 這是指向 FILE 對(duì)象的指針泥从,該 FILE 對(duì)象指定了一個(gè)輸入流。
facebook::react::BundleHeader header;
size_t readResult = fread(&header, sizeof(header), 1, bundle);
fclose(bundle);
facebook::react::ScriptTag tag = facebook::react::parseTypeFromHeader(header);
switch (tag) {
case facebook::react::ScriptTag::RAMBundle:
break;
case facebook::react::ScriptTag::String: {
NSData *source = [NSData dataWithContentsOfFile:scriptURL.path
options:NSDataReadingMappedIfSafe
error:error];
if (sourceLength && source != nil) {
*sourceLength = source.length;
}
return source;
}
case facebook::react::ScriptTag::BCBundle:
break;
}
struct stat statInfo;
if (sourceLength) {
*sourceLength = statInfo.st_size;
}
return [NSData dataWithBytes:&header length:sizeof(header)];
}
如上迫皱,主要做了3件事:
1.如果bundle不在本地歉闰,那么就不能同步加載,寫入error
2.檢查前4個(gè)字節(jié)來(lái)獲取這個(gè)bundle類型卓起,類型信息存在ScriptTag中
3.返回bundle data
異步加載bundle
上面介紹了同步加載bundle就是讀取本地磁盤預(yù)置或預(yù)先下載的bundle數(shù)據(jù),所以不難判斷異步加載bundle就是下載網(wǎng)絡(luò)上的bundle凹炸。下面我們來(lái)看下源碼:
// RCTJavaScriptLoader.mm
static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoadProgressBlock onProgress, RCTSourceLoadBlock onComplete)
{
if (scriptURL.fileURL) {
// Reading in a large bundle can be slow. Dispatch to the background queue to do it.
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;
}
RCTMultipartDataTask *task = [[RCTMultipartDataTask alloc] initWithURL:scriptURL partHandler:^(NSInteger statusCode, NSDictionary *headers, NSData *data, NSError *error, BOOL done) {
if (!done) {
if (onProgress) {
onProgress(progressEventFromData(data));
}
return;
}
// 驗(yàn)證服務(wù)器返回的是不是JavaScript
NSString *contentType = headers[@"Content-Type"];
NSString *mimeType = [[contentType componentsSeparatedByString:@";"] firstObject];
if (![mimeType isEqualToString:@"application/javascript"] &&
![mimeType isEqualToString:@"text/javascript"]) {
NSString *description = [NSString stringWithFormat:@"Expected MIME-Type to be 'application/javascript' or 'text/javascript', but got '%@'.", mimeType];
error = [NSError errorWithDomain:@"JSServer"
code:NSURLErrorCannotParseResponse
userInfo:@{
NSLocalizedDescriptionKey: description,
@"headers": headers,
@"data": data
}];
onComplete(error, nil);
return;
}
// 把data包裝成source對(duì)象
RCTSource *source = RCTSourceCreate(scriptURL, data, data.length);
parseHeaders(headers, source);
onComplete(nil, source);
} progressHandler:^(NSDictionary *headers, NSNumber *loaded, NSNumber *total) {
// Only care about download progress events for the javascript bundle part.
if ([headers[@"Content-Type"] isEqualToString:@"application/javascript"]) {
onProgress(progressEventFromDownloadProgress(loaded, total));
}
}];
[task startTask];
}
如上戏阅,不難看出,異步加載主要做了2件事情:
1.如果bundle是本地文件則異步加載本地bundle
2.如果不是本地bundle則開啟一個(gè)RCTMultipartDataTask異步下載
以上啤它,是RN所有加載JS代碼的邏輯奕筐。接下來(lái)介紹native是如何執(zhí)行JS代碼的。
執(zhí)行JS代碼
執(zhí)行JS代碼变骡,終于走到這一步了离赫,還是要從RCTCxxBridge的start方法說(shuō)起,如下:
// RCTCxxBridge.mm
- (void)start
{
// 此處省略若干行...
// Wait for both the modules and source code to have finished loading
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];
}
});
}
不難看出塌碌,start方法在最后通過(guò)調(diào)用executeSourceCode:執(zhí)行JS代碼渊胸,executeSourceCode:源碼如下:
// RCTCxxBridge.mm
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
{
dispatch_block_t completion = ^{
// 在主線程上執(zhí)行狀態(tài)更新和通知,這樣我們就不會(huì)遇到RCTRootView的時(shí)序問(wèn)題
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptDidLoadNotification
object:self->_parentBridge userInfo:@{@"bridge": self}];
});
};
// 根據(jù)sync來(lái)選擇執(zhí)行JS的方式(同步台妆、異步)
if (sync) {
[self executeApplicationScriptSync:sourceCode url:self.bundleURL];
completion();
} else {
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
}
}
如上翎猛,代碼主要做了兩件事:
1.準(zhǔn)備一個(gè)completion的block胖翰,在JS執(zhí)行完成后回調(diào)
2.根據(jù)sync來(lái)選擇是同步執(zhí)行還是異步執(zhí)行JS
通過(guò)看這兩個(gè)函數(shù)的實(shí)現(xiàn),不難發(fā)現(xiàn)切厘,最終他們都是調(diào)用了同一個(gè)方法萨咳,如下:
// RCTCxxBridge.mm
- (void)executeApplicationScript:(NSData *)script
url:(NSURL *)url
async:(BOOL)async
{
[self _tryAndHandleError:^{
NSString *sourceUrlStr = deriveSourceURL(url);
// 發(fā)送 將要執(zhí)行JS 的通知 RCTJavaScriptWillStartExecutingNotification
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptWillStartExecutingNotification
object:self->_parentBridge userInfo:@{@"bridge": self}];
// 如果是RAMBundle則調(diào)用_reactInstance的loadRAMBundle:方法
// 否則調(diào)用_reactInstance的loadScriptFromString:方法
if (isRAMBundle(script)) {
[self->_performanceLogger markStartForTag:RCTPLRAMBundleLoad];
auto ramBundle = std::make_unique<JSIndexedRAMBundle>(sourceUrlStr.UTF8String);
std::unique_ptr<const JSBigString> scriptStr = ramBundle->getStartupCode();
[self->_performanceLogger markStopForTag:RCTPLRAMBundleLoad];
[self->_performanceLogger setValue:scriptStr->size() forTag:RCTPLRAMStartupCodeSize];
if (self->_reactInstance) {
auto registry = RAMBundleRegistry::multipleBundlesRegistry(std::move(ramBundle), JSIndexedRAMBundle::buildFactory());
self->_reactInstance->loadRAMBundle(std::move(registry), std::move(scriptStr),
sourceUrlStr.UTF8String, !async);
}
} else if (self->_reactInstance) {
self->_reactInstance->loadScriptFromString(std::make_unique<NSDataBigString>(script),
sourceUrlStr.UTF8String, !async);
} else {
std::string methodName = async ? "loadApplicationScript" : "loadApplicationScriptSync";
throw std::logic_error("Attempt to call " + methodName + ": on uninitialized bridge");
}
}];
}
如我在以上源碼中加的注釋所述,這個(gè)方法做了2件事:
1.發(fā)送一個(gè)將要執(zhí)行JS的通知 名為RCTJavaScriptWillStartExecutingNotification
2.根據(jù)bundle的類型(是否為RAMBundle)分別調(diào)用_reactInstance的不同方法。如果是RAMBundle則調(diào)用_reactInstance的loadRAMBundle:方法昵观,否則調(diào)用_reactInstance的loadScriptFromString:方法荒典。因篇幅問(wèn)題,因篇幅問(wèn)題舀凛,此處不對(duì)RAM bundle展開介紹。
// Instance.cpp
// load普通的 JS bundle
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously) {
if (loadSynchronously) {
loadApplicationSync(nullptr, std::move(string), std::move(sourceURL));
} else {
loadApplication(nullptr, std::move(string), std::move(sourceURL));
}
}
// load RAM bundle
void Instance::loadRAMBundle(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL,
bool loadSynchronously) {
if (loadSynchronously) {
loadApplicationSync(std::move(bundleRegistry), std::move(startupScript),
std::move(startupScriptSourceURL));
} else {
loadApplication(std::move(bundleRegistry), std::move(startupScript),
std::move(startupScriptSourceURL));
}
}
如上员萍,我們發(fā)現(xiàn)無(wú)論是加載RAM bundle還是加載普通的bundle都調(diào)用了Instance的loadApplicationSync或loadApplication方法腾降。下面我們來(lái)看這兩個(gè)方法:
// Instance.cpp
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));
}
void Instance::loadApplicationSync(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
nativeToJsBridge_->loadApplicationSync(std::move(bundleRegistry), std::move(string),
std::move(sourceURL));
}
如上,我們發(fā)現(xiàn)碎绎,Instance執(zhí)行JS的方法又調(diào)用到了nativeToJsBridge這一層:
// NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
runOnExecutorQueue(
[this,
bundleRegistryWrap=folly::makeMoveWrapper(std::move(bundleRegistry)),
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL=std::move(startupScriptSourceURL)]
(JSExecutor* executor) mutable {
auto bundleRegistry = bundleRegistryWrap.move();
// 如果是RAM bundle則把 bundle 傳給 executor
if (bundleRegistry) {
executor->setBundleRegistry(std::move(bundleRegistry));
}
// 調(diào)用JSIExecutor加載腳本
try {
executor->loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
} catch (...) {
m_applicationScriptHasFailure = true;
throw;
}
});
}
void NativeToJsBridge::loadApplicationSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
if (bundleRegistry) {
m_executor->setBundleRegistry(std::move(bundleRegistry));
}
try {
m_executor->loadApplicationScript(std::move(startupScript),
std::move(startupScriptSourceURL));
} catch (...) {
m_applicationScriptHasFailure = true;
throw;
}
}
如上螃壤,loadApplication和loadApplicationSync這兩個(gè)方法實(shí)現(xiàn)基本一致,都是調(diào)用了成員變量m_executor的loadApplicationScript方法筋帖,區(qū)別在于loadApplication把代碼放到了m_executorMessageQueueThread中去執(zhí)行奸晴,而loadApplicationSync在當(dāng)前線程執(zhí)行。讓我們來(lái)看下JSIExecutor(生產(chǎn)環(huán)境)的loadApplicationScript的實(shí)現(xiàn):
// 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); }));
// 最終調(diào)用到JavaScriptCore的JSEvaluateScript函數(shù)
runtime_->evaluateJavaScript(
std::make_unique<BigStringBuffer>(std::move(script)), sourceURL);
flush();
}
如上日麸,loadApplicationScript方法主要做了3件事:
1.將Native側(cè)的NativeModuleProxy對(duì)象注入到global的nativeModuleProxy上寄啼,相當(dāng)于global.nativeModuleProxy = new NativeModuleProxy。
2.然后向global中注入了nativeFlushQueueImmediate代箭,nativeCallSyncHook 2個(gè)方法墩划。相當(dāng)于global.nativeFlushQueueImmediate = JSIExecutor::callNativeModules(args); global.nativeCallSyncHook = JSIExecutor::nativeCallSyncHook(args);注入成功后嗡综,JS側(cè)的global調(diào)用nativeFlushQueueImmediate或nativeCallSyncHook這兩個(gè)方法就會(huì)直接調(diào)用到JSIExecutor對(duì)應(yīng)的方法上乙帮。
3.調(diào)用runtime_->evaluateJavaScript方法,最終調(diào)用到JavaScriptCore的JSEvaluateScript函數(shù)极景。對(duì)JavaScriptCore了解的開發(fā)者應(yīng)該都知道JSEvaluateScript的作用就是在JS環(huán)境中執(zhí)行JS代碼察净。
4.JS腳本執(zhí)行完成,執(zhí)行flush操作盼樟。flush函數(shù)的主要作用就是執(zhí)行JS側(cè)的隊(duì)列中緩存的對(duì)native的方法調(diào)用氢卡。
evaluateJavaScript
上面說(shuō)runtime最終調(diào)用到了JavaScriptCore的JSEvaluateScript函數(shù)。讓我們?cè)賮?lái)看下JSCRuntime的evaluateJavaScript實(shí)現(xiàn):
// JSCRumtime.cpp
jsi::Value JSCRuntime::evaluateJavaScript(
const std::shared_ptr<const jsi::Buffer> &buffer,
const std::string& sourceURL) {
std::string tmp(
reinterpret_cast<const char*>(buffer->data()), buffer->size());
JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str());
JSStringRef sourceURLRef = nullptr;
if (!sourceURL.empty()) {
sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str());
}
JSValueRef exc = nullptr;
JSValueRef res =
JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc);
return createValue(res);
}
不難看出晨缴,最終還是調(diào)用的JavaScriptCore的JSEvaluateScript這個(gè)函數(shù)來(lái)執(zhí)行JS代碼译秦。對(duì)JavaScriptCore或Hybrid開發(fā)有了解的人應(yīng)該對(duì)這個(gè)函數(shù)非常熟悉,這個(gè)函數(shù)最關(guān)鍵的是前兩個(gè)參數(shù)ctx和script,分別代表JS執(zhí)行環(huán)境和將要執(zhí)行的腳本字符串诀浪。
flush
在ctx中執(zhí)行JS源碼棋返,會(huì)初始化JS環(huán)境,BatchedBridge.js,NativeModules.js中的初始化代碼也會(huì)執(zhí)行雷猪。在BatchedBridge.js中睛竣,創(chuàng)建了一個(gè)名為BatchedBridge的MessageQueue,并設(shè)置到global的__fbBatchedBridge屬性里求摇,這個(gè)屬性后面會(huì)用到射沟。在初始化JS環(huán)境的時(shí)候,會(huì)加載到某些NativeModule与境,這些module才會(huì)被初始化验夯,即調(diào)用到native側(cè)JSINativeModules的getModule方法。當(dāng)相關(guān)的Module都加載完之后摔刁,evaluateScript方法執(zhí)行完挥转,JS環(huán)境初始化完畢。然后就到執(zhí)行flush方法共屈。如下:
// JSIExecutor.cpp
void JSIExecutor::flush() {
if (flushedQueue_) {
callNativeModules(flushedQueue_->call(*runtime_), true);
return;
}
Value batchedBridge =
runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
if (!batchedBridge.isUndefined()) {
bindBridge();
callNativeModules(flushedQueue_->call(*runtime_), true);
} else if (delegate_) {
callNativeModules(nullptr, true);
}
}
上面的邏輯是:
1.如果JSIExecutor的flushedQueue_函數(shù)不為空绑谣,則通過(guò)函數(shù)flushedQueue_獲取待調(diào)用的方法queue,然后執(zhí)行callNativeModules拗引。(第一次調(diào)用flush()時(shí)借宵,flushedQueue_必為空,稍后在bindBridge()中才bind flushedQueue_)
2.以"__fbBatchedBridge"作為屬性key去global中取對(duì)應(yīng)的值也就是batchedBridge矾削,batchedBridge本質(zhì)上是JS側(cè)的MessageQueue類實(shí)例化的一個(gè)對(duì)象
3.如果獲取到了JS側(cè)定義的batchedBridge對(duì)象壤玫,則執(zhí)行bindBridge操作(我們知道在JS初始化環(huán)境的時(shí)候,JS的batchedBridge這個(gè)值已經(jīng)被初始化為MessageQueue對(duì)象哼凯,在BatchedBridge.js中欲间,創(chuàng)建了一個(gè)名為BatchedBridge的MessageQueue對(duì)象,并設(shè)置到global的__fbBatchedBridge屬性里)断部,即把batchedBridge中的方法和Native側(cè)JSIExecutor的方法進(jìn)行綁定括改。這些bind操作本質(zhì)上是native指針指向JS函數(shù)。例如:把batchedBridge中的callFunctionReturnFlushedQueue 和 JSIExecutor對(duì)象的callFunctionReturnFlushedQueue_進(jìn)行綁定家坎;把batchedBridge中的invokeCallbackAndReturnFlushedQueue 和 JSIExecutor中的invokeCallbackAndReturnFlushedQueue_進(jìn)行綁定;把batchedBridge中的flushedQueue 和 JSIExecutor中的flushedQueue_進(jìn)行綁定吝梅。把batchedBridge中的callFunctionReturnResultAndFlushedQueue 和 JSIExecutor中的callFunctionReturnResultAndFlushedQueue_進(jìn)行綁定虱疏。bind完成之后,執(zhí)行callNativeModules方法苏携。
4.如果沒(méi)有獲取到JS側(cè)定義的batchedBridge對(duì)象做瞪,則直接執(zhí)行callNativeModules方法,即沒(méi)有bind操作。
讓我們繼續(xù)看bindBridge方法的實(shí)現(xiàn)吧:
// JSIExecutor.cpp
void JSIExecutor::bindBridge() {
std::call_once(bindFlag_, [this] {
Value batchedBridgeValue =
runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "callFunctionReturnFlushedQueue");
invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "invokeCallbackAndReturnFlushedQueue");
flushedQueue_ =
batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
callFunctionReturnResultAndFlushedQueue_ =
batchedBridge.getPropertyAsFunction(
*runtime_, "callFunctionReturnResultAndFlushedQueue");
});
}
bindBridge把global中的函數(shù)變量賦值給了JSIExecutor的成員變量指針装蓬,這為后面Native call JS做好了準(zhǔn)備著拭。
Native調(diào)用JS
上面說(shuō)了bindBridge方法中把global.batchedBridge中的方法和Native側(cè)JSIExecutor的方法進(jìn)行綁定。本質(zhì)上就是Native指針指向JS函數(shù)(例如:JSIExecutor::callFunctionReturnFlushedQueue_ = global.batchedBridge.callFunctionReturnFlushedQueue)牍帚。這樣就可以在native側(cè)直接調(diào)用到JS函數(shù)儡遮,實(shí)現(xiàn)native調(diào)用JS。本節(jié)將從源碼的角度介紹Native調(diào)用JS的相關(guān)細(xì)節(jié)暗赶。
執(zhí)行完JS源碼完成后鄙币,在RCTCxxBridge中會(huì)發(fā)送一個(gè)名為RCTJavaScriptDidLoadNotification的通知。如下:
// RCTCxxBridge.mm
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
{
// 可以在任何執(zhí)行JS的線程調(diào)用
dispatch_block_t completion = ^{
// 在主線程上執(zhí)行狀態(tài)更新和通知蹂随,這樣我們就不會(huì)遇到RCTRootView的時(shí)序問(wèn)題
dispatch_async(dispatch_get_main_queue(), ^{
// 主線程發(fā)送一個(gè)通知
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptDidLoadNotification
object:self->_parentBridge userInfo:@{@"bridge": self}];
});
};
if (sync) {
[self executeApplicationScriptSync:sourceCode url:self.bundleURL];
completion();
} else {
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
}
}
RCTRootView監(jiān)聽到這個(gè)通知后執(zhí)行了javaScriptDidLoad:方法十嘿,然后沿著Instance->NativeToJsBridge->JSIExecutor這個(gè)調(diào)用鏈調(diào)用了JSIExecutor::callFunction方法,方法內(nèi)調(diào)用了JSIExecutor的callFunctionReturnFlushedQueue_方法岳锁,bindBridge一節(jié)中介紹了callFunctionReturnFlushedQueue_是通過(guò)runtime將native指針指向JS函數(shù)绩衷。所以,就相當(dāng)于調(diào)用JS MessageQueue的callFunctionReturnFlushedQueue方法激率,該方法接收調(diào)用JS方法所需的moduleId咳燕、methodId和參數(shù),執(zhí)行完畢后JS會(huì)給Native返回一個(gè)queue柱搜,該queue中是一系列JS需要native側(cè)執(zhí)行的方法迟郎。最后這個(gè)queue被交給callNativeModules進(jìn)行調(diào)用。詳細(xì)調(diào)用過(guò)程如下:
// RCTRootView.m
- (void)javaScriptDidLoad:(NSNotification *)notification
{
RCTBridge *bridge = notification.userInfo[@"bridge"];
if (bridge != _contentView.bridge) {
[self bundleFinishedLoading:bridge];
}
}
- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
bridge:bridge
reactTag:self.reactTag
sizeFlexiblity:_sizeFlexibility];
[self runApplication:bridge];
[self insertSubview:_contentView atIndex:0];
}
- (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
};
// 調(diào)用RCTCxxBridge的enqueueJSCall:method:args:completion:方法
[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
}
// NativeToJsBridge.cpp
// 該方法可以在任何線程調(diào)用
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion
{
__weak __typeof(self) weakSelf = self;
[self _runAfterLoad:^(){
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf->_reactInstance) {
// 調(diào)用Instance的callJSFunction方法
strongSelf->_reactInstance->callJSFunction([module UTF8String], [method UTF8String],
convertIdToFollyDynamic(args ?: @[]));
// ensureOnJavaScriptThread may execute immediately, so use jsMessageThread, to make sure
// the block is invoked after callJSFunction
if (completion) {
if (strongSelf->_jsMessageThread) {
strongSelf->_jsMessageThread->runOnQueue(completion);
} else {
RCTLogWarn(@"Can't invoke completion without messageThread");
}
}
}
}];
}
// Instance.cpp
void Instance::callJSFunction(std::string &&module, std::string &&method,
folly::dynamic &¶ms) {
callback_->incrementPendingJSCalls();
// 調(diào)用NativeToJSBridge的callFunction方法
nativeToJsBridge_->callFunction(std::move(module), std::move(method),
std::move(params));
}
// NativeToJsBridge.cpp
void NativeToJsBridge::callFunction(
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
runOnExecutorQueue([this, module = std::move(module), method = std::move(method), arguments = std::move(arguments), systraceCookie]
(JSExecutor* executor) {
// 調(diào)用JSIExecutor的callFunction方法
executor->callFunction(module, method, arguments);
});
}
// JSIExecutor.cpp
void JSIExecutor::callFunction(
const std::string &moduleId,
const std::string &methodId,
const folly::dynamic &arguments) {
Value ret = Value::undefined();
try {
scopedTimeoutInvoker_(
[&] {
// 調(diào)用callFunctionReturnFlushedQueue_并把JS需要Native側(cè)執(zhí)行的方法queue作為返回值返回聪蘸,賦值給ret
// 傳入JS moduleId宪肖、methodId、arguements
// 返回值 queue
ret = callFunctionReturnFlushedQueue_->call(
*runtime_,
moduleId,
methodId,
valueFromDynamic(*runtime_, arguments));
},
std::move(errorProducer));
} catch (...) {
std::throw_with_nested(
std::runtime_error("Error calling " + moduleId + "." + methodId));
}
// native側(cè)刷新queue中需要執(zhí)行的方法
callNativeModules(ret, true);
}
上面介紹了通過(guò)MessageQueue的callFunctionReturnFlushedQueue實(shí)現(xiàn)Native調(diào)用JS健爬。除此之外還有其他3個(gè)與Native call JS相關(guān)的函數(shù)控乾,我們已經(jīng)在bindBridge中見過(guò)了。他們分別是:invokeCallbackAndReturnFlushedQueue娜遵、flushedQueue蜕衡、callFunctionReturnResultAndFlushedQueue∩枘猓看名字就知道他們的作用慨仿,這里不做詳細(xì)介紹。 接下來(lái)JS的MessageQueue.js對(duì)callFunctionReturnFlushedQueue的實(shí)現(xiàn):
// MessageQueue.js
callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) {
this.__guard(() => {
this.__callFunction(module, method, args);
});
return this.flushedQueue();
}
__callFunction(module: string, method: string, args: any[]): any {
const moduleMethods = this.getCallableModule(module);
const result = moduleMethods[method].apply(moduleMethods, args);
return result;
}
flushedQueue() {
this.__guard(() => {
this.__callImmediates();
});
const queue = this._queue;
this._queue = [[], [], [], this._callID];
return queue[0].length ? queue : null;
}
可以看出callFunctionReturnFlushedQueue主要做了兩件事:
1.通過(guò)moduleId和methodId完成方法的調(diào)用(__callFunction函數(shù))
2.返回一個(gè)queue(flushedQueue函數(shù))
至此纳胧,Native調(diào)用JS相關(guān)的實(shí)現(xiàn)基本介紹完了镰吆。讓我們總結(jié)下:
JS代碼執(zhí)行JS上下文環(huán)境都已經(jīng)初始化,MessagQueue相關(guān)代碼也會(huì)被調(diào)用跑慕,然后會(huì)通過(guò)runtime讓Native指針指向JS函數(shù)万皿,后面Native call JS都是通過(guò)這4個(gè)函數(shù)完成的摧找。JS代碼執(zhí)行完畢后,RCTCxxBridge會(huì)發(fā)送一個(gè)名為RCTJavaScriptDidLoadNotification的通知給RCTRootView牢硅。然后經(jīng)過(guò)RCTRootView->RCTBridge->RCTCxxBridge->Instance->NativeToJsBridge->JSIExecutor層層調(diào)用蹬耘,最終通過(guò)調(diào)用callFunctionReturnFlushedQueue完成Native對(duì)JS的調(diào)用。Native call JS的四個(gè)核心函數(shù)如下减余,至此Native call JS基本介紹完了综苔。
flushedQueue
callFunctionReturnFlushedQueue
invokeCallbackAndReturnFlushedQueue
callFunctionReturnResultAndFlushedQueue
JS調(diào)用Native
前面說(shuō)過(guò),JS中g(shù)lobal.__fbGenNativeModule屬性其實(shí)就是NativeModules.js中定義的genModule函數(shù)佳励。在加載JS腳本的時(shí)候休里,將JSINativeModule的m_genNativeModuleJS指向了global.__fbGenNativeModule。即global.__fbGenNativeModule == genModule == m_genNativeModuleJS赃承。在執(zhí)行JS源碼時(shí)候妙黍,最終會(huì)調(diào)用到JSIExecutor::loadApplicationScript方法。這個(gè)方法中初始化了一個(gè)nativeModuleProxy對(duì)象并設(shè)置給了global.nativeModuleProxy瞧剖。初始化nativeModuleProxy對(duì)象會(huì)觸發(fā)nativeModuleProxy的get方法拭嫁,get方法最終調(diào)用到JSINativeModules的createModule方法,createModule中調(diào)用了m_genNativeModuleJS方法即JS側(cè)genModule函數(shù)抓于,方法入?yún)⑹莕ative側(cè)的ModuleConfig對(duì)象做粤,返回值是moduleInfo(形如:{modulename, moduleInfo},moduleName是模塊名捉撮,moduleInfo是這個(gè)模塊信息怕品,包括模塊方法)。JS側(cè)拿到這個(gè)返回值進(jìn)行緩存巾遭,后續(xù)通過(guò)緩存的這個(gè)moduleInfo獲取native側(cè)的模塊配置肉康,進(jìn)而調(diào)用native方法,例如NativeModule.moduleName.methodName灼舍。
需要說(shuō)明的是吼和,通常情況下,JS是不會(huì)“直接的”調(diào)用OC方法的骑素。當(dāng)我們?cè)贘S中通過(guò)NativeModule調(diào)用native方法時(shí)炫乓,模塊ID和方法ID會(huì)被加入一個(gè)名為_queue的隊(duì)列,等到native側(cè)調(diào)用JS方法時(shí)献丑,順便把這個(gè)隊(duì)列作為返回值返回給native側(cè)末捣。Native側(cè)再一一解析隊(duì)列中的每一個(gè)moduleID和methodID后,封裝成NSInvocation完成調(diào)用创橄。如下是JS調(diào)用Native的流程圖:
我們知道MessageQueue.js承接了Native和JS通信的任務(wù)塔粒。在Native調(diào)用JS一節(jié)中我們知道了callFunctionReturnFlushedQueue這個(gè)函數(shù)用于Native call JS,并把JS中的queue返回給Native筐摘。這個(gè)queue存儲(chǔ)了一系列JS對(duì)Native的調(diào)用。native側(cè)拿到這個(gè)queue后,就會(huì)解析這個(gè)queue中的內(nèi)容咖熟,得到相關(guān)模塊的配置和參數(shù)圃酵,并進(jìn)行動(dòng)態(tài)調(diào)用。iOS上的調(diào)用主要是把這些配置和參數(shù)封裝NSInvocation實(shí)例馍管,進(jìn)行調(diào)用郭赐。其調(diào)用順序大致如下:
// JSIExecutor.app
void JSIExecutor::callNativeModules(const Value &queue, bool isEndOfBatch) {
// delegate_是JsToNativeBridge類型的實(shí)例
delegate_->callNativeModules(
*this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
}
// JsToNativeBridge.cpp
void callNativeModules(
__unused JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
for (auto& call : parseMethodCalls(std::move(calls))) {
m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
}
// ModuleRegistry.cpp
void ModuleRegistry::callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) {
modules_[moduleId]->invoke(methodId, std::move(params), callId);
}
// RCTNativeModule.mm
void RCTNativeModule::invoke(unsigned int methodId, folly::dynamic &¶ms, int callId) {
__weak RCTBridge *weakBridge = m_bridge;
__weak RCTModuleData *weakModuleData = m_moduleData;
invokeInner(weakBridge, weakModuleData, methodId, std::move(params));
}
// RCTNativeModule.mm
static MethodCallResult invokeInner(RCTBridge *bridge, RCTModuleData *moduleData, unsigned int methodId, const folly::dynamic ¶ms) {
id<RCTBridgeMethod> method = moduleData.methods[methodId];
NSArray *objcParams = convertFollyDynamicToId(params);
id result = [method invokeWithBridge:bridge module:moduleData.instance arguments:objcParams];
return convertIdToFollyDynamic(result);
}
// RCTModuleMethod.mm
- (id)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments
{
[_invocation invokeWithTarget:module];
}
以上,是JS call Native 的方法調(diào)用鏈确沸,主要是JSIExecutor先收到JS側(cè)的調(diào)用請(qǐng)求捌锭,然后將請(qǐng)求轉(zhuǎn)發(fā)給JSIExecutor實(shí)例的m_delegate,也就是調(diào)用m_delegate的callNativeModules方法罗捎,m_delegate本質(zhì)上就是一個(gè)JSToNativeBridge實(shí)例观谦,他是在NativeToJSBridge的初始化列表中初始化JSIExecutor時(shí)傳給JSIExecutor的。m_delegate繼續(xù)調(diào)用m_registry的callNativeMethod方法桨菜,m_registry是m_delegate的一個(gè)成員變量豁状,也就是JSToNativeBridge的成員變量。然后調(diào)用經(jīng)由NativeModule和RCTBridgeMethod轉(zhuǎn)發(fā)倒得,最后在RCTBridgeMethod中通過(guò)NSInvocation的方式完成了native側(cè)方法的調(diào)用泻红。
至此,JS調(diào)用Native方法就介紹完了霞掺。
三個(gè)疑問(wèn)
你可能有3個(gè)疑問(wèn):
1.為什么JS不同步調(diào)用native方法而選擇異步谊路?
2.為什么RN不主動(dòng)調(diào)用JS而是把調(diào)用緩存到隊(duì)列,而是等native call JS時(shí)再把隊(duì)列以返回值的形式返回給native菩彬?這樣JS還能跑的通嗎缠劝?
3.設(shè)計(jì)成這樣如何保證native側(cè)的方法可以得到及時(shí)的調(diào)用?
對(duì)于第一個(gè)問(wèn)題:我們知道JS代碼是運(yùn)行在JS線程而非main thread挤巡,并且JS是單線程剩彬,如果同步調(diào)用native方法就會(huì)block住JS代碼的運(yùn)行,所以RN選擇了JS和Native異步通信矿卑。
對(duì)于第二個(gè)問(wèn)題:JS不會(huì)主動(dòng)傳遞數(shù)據(jù)給OC喉恋,在調(diào)OC方法時(shí),會(huì)把ModuleID母廷,MethodID等數(shù)據(jù)加到一個(gè)隊(duì)列里轻黑,等OC過(guò)來(lái)調(diào)JS的任意方法時(shí),再把這個(gè)隊(duì)列返回給OC琴昆,此時(shí)OC再執(zhí)行這個(gè)隊(duì)列里要調(diào)用的方法氓鄙。讓我們回顧下iOS的事件傳遞和響應(yīng)機(jī)制就會(huì)恍然大悟,在Native開發(fā)中业舍,只在有事件觸發(fā)的時(shí)候抖拦,才會(huì)調(diào)用native代碼升酣。這個(gè)事件可以是啟動(dòng)事件、觸摸事件态罪、滾動(dòng)事件噩茄、timer事件、系統(tǒng)事件复颈、回調(diào)事件绩聘。而在React Native里,本質(zhì)上JSX Component最終都是native view耗啦。這些事件發(fā)生時(shí)OC都會(huì)調(diào)用JS側(cè)相應(yīng)的方法以保證JS側(cè)和native側(cè)保持同步凿菩,JS處理完這些事件后再執(zhí)行JS想讓OC執(zhí)行的方法,而沒(méi)有事件發(fā)生的時(shí)候帜讲,是不會(huì)執(zhí)行任何代碼的衅谷,這跟native開發(fā)里事件響應(yīng)機(jī)制是一致的。在native調(diào)用JS的時(shí)候舒帮,JS把需要native處理的方法隊(duì)列返回給native側(cè)讓native去處理会喝。這樣既保證了JS和native側(cè)事件和邏輯的同步,JS也可以趁機(jī)搭車call native玩郊,避免了JS和native側(cè)頻繁的通信肢执。 RN的這種設(shè)計(jì)還是很合理的,真的是巧奪天工译红,這種設(shè)計(jì)思路還是值得借鑒的预茄。
對(duì)于第三個(gè)問(wèn)題:JS只是被動(dòng)的等待native call JS,然后趁機(jī)把隊(duì)列返回給native侦厚,那么如何保證方法調(diào)用的及時(shí)性呢耻陕?換句話說(shuō),如果native遲遲不調(diào)用JS刨沦,那JS側(cè)隊(duì)列中一大堆方法舊只能干等著嗎诗宣?答案當(dāng)然不是這樣的,JS規(guī)定了一個(gè)時(shí)間閾值想诅,這閾值是5ms召庞,如果超過(guò)5ms后依舊沒(méi)有native call JS。那么JS就會(huì)主動(dòng)觸發(fā)隊(duì)列的刷新来破,即立即讓native側(cè)執(zhí)行隊(duì)列中緩存的一系列的方法篮灼。JS側(cè)觸發(fā)native的源碼如下:
// MessageQueue.js
const MIN_TIME_BETWEEN_FLUSHES_MS = 5;
enqueueNativeCall(
moduleID: number,
methodID: number,
params: any[],
onFail: ?Function,
onSucc: ?Function,
) {
this.processCallbacks(moduleID, methodID, params, onFail, onSucc);
this._queue[MODULE_IDS].push(moduleID);
this._queue[METHOD_IDS].push(methodID);
this._queue[PARAMS].push(params);
const now = Date.now();
if (
global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS
) {
const queue = this._queue;
this._queue = [[], [], [], this._callID];
this._lastFlush = now;
global.nativeFlushQueueImmediate(queue);
}
}
如上,每次JS想要call native徘禁,都會(huì)判斷距上一次flush queue的時(shí)間間隔诅诱,如果間隔大于或等于5ms,就調(diào)用global.nativeFlushQueueImmediate方法送朱,把queue作為入?yún)鹘onative娘荡。
大結(jié)局
好了干旁,雖然講了很多,也很啰嗦它改,但事無(wú)巨細(xì)疤孕,尤其是對(duì)于RN這種龐大的工程框架,一篇文章是很難覆蓋各個(gè)細(xì)節(jié)的央拖。對(duì)于一些重要的細(xì)節(jié),日后找機(jī)會(huì)寫文章單獨(dú)分析鹉戚。最后總結(jié)一下RN的初始化啟動(dòng)流程:
RN的啟動(dòng)流程主要分4個(gè)階段:bridge初始化階段鲜戒、源碼加載階段、源碼執(zhí)行階段抹凳、運(yùn)行JSApplication階段
bridge初始化階段
bridge初始化階段主要是初始化并配置了RCTBridge實(shí)例遏餐、RCTCxxBridge實(shí)例、Instance實(shí)例赢底、NativeToJSBridge實(shí)例失都、JSIExecutor實(shí)例。
- 在APPDelegate的啟動(dòng)方法中創(chuàng)建了RCTBridge和一個(gè)RCTRootView幸冻,然后在RCTBridge中創(chuàng)建了一個(gè)名為batchedBridge的RCTCxxBridge實(shí)例粹庞,并調(diào)用了self.batchedBridge start方法。在start方法里先創(chuàng)建了一個(gè)專門用來(lái)運(yùn)行JS代碼的JS線程洽损,這個(gè)線程綁定到了一個(gè)runloop以免退出庞溜。然后將所有注冊(cè)的module生成RCTModuleData,并根據(jù)需要調(diào)用他們的初始化方法碑定,以上操作都是在main thread中進(jìn)行的流码。
- 接下來(lái)一邊在JS線程中執(zhí)行Intance的初始化方法,一邊異步進(jìn)行JS源碼的加載延刘。Intance的初始化主要是在Instance::initializeBridge方法里創(chuàng)建了一個(gè)名為nativeToJsBridge_的NativeToJSBridge實(shí)例變量漫试。然后NativeToJsBridge的初始化方法里通過(guò)executorFactory創(chuàng)建了一個(gè)名為m_executor的JSIExecutor實(shí)例并傳入了一個(gè)runtime變量。至此碘赖,Instance驾荣、NativeToJSBridge、JSIExecutor初始化完成崖疤。
源碼加載階段
start方法中初始化Instance的同時(shí)還在load JS源碼秘车,JS源碼加載分為同步和異步。JS bundle類型分為RAM bundle和普通bundle劫哼。load完成后返回JS sourceCode進(jìn)入源碼執(zhí)行階段叮趴。
源碼執(zhí)行階段
當(dāng)JS sourceCode加載完成且native module也初始化也完成后,就會(huì)繼續(xù)走執(zhí)行JS源碼的邏輯权烧,主要是初始化JS上下文環(huán)境眯亦,并建立Native call JS的能力伤溉,即Native指針指向JS函數(shù)。
- start方法先是調(diào)用executeSourceCode方法妻率,然后經(jīng)過(guò)RCTCxxBridge -> Instance -> NativeToJsBridge -> JSIExecutor層層調(diào)用最終會(huì)執(zhí)行到JSIExecutor::loadApplicationScript方法乱顾。在JSIExecutor::loadApplicationScript中通過(guò)runtime向global中注入了nativeModuleProxy。并且將JSIExecutor::nativeFlushQueueImmediate宫静、JSIExecutor::nativeCallSyncHook注入到global中走净。
- 向global注入nativeModuleProxy的時(shí)候,會(huì)觸發(fā)nativeModuleProxy的get方法孤里,目的是生成native module的配置信息并返回給JS伏伯。 主要邏輯是通過(guò)nativeModuleProxy -> JSINativeModules.getModule -> JSINativeModules.createModule層層調(diào)用,最后調(diào)用JS在global中注冊(cè)的genModule這個(gè)方法把native module配置信息生成對(duì)應(yīng)的JS對(duì)象捌袜,JS會(huì)緩存這個(gè)對(duì)象用于將來(lái)調(diào)用native说搅。當(dāng)然native側(cè)早就有了一份module配置信息表,這個(gè)表存儲(chǔ)在ModuleRegistry中虏等。日后JS call Native 都是傳遞的模塊ID和方法ID弄唧,然后native再根據(jù)ID去查表完成方法調(diào)用。
- 在JSIExecutor::loadApplicationScript中會(huì)調(diào)用JSCRumtime的evaluteJavaScript方法霍衫,進(jìn)而調(diào)用到JavaScriptCore的JSEvaluateScript方法執(zhí)行JS源碼候引。
- 在JS源碼執(zhí)行完后,JSIExecutor::loadApplicationScript中會(huì)調(diào)用flush()方法刷新JS的messageQueue中緩存的方法隊(duì)列慕淡。
運(yùn)行JS Application階段
上面JS腳本執(zhí)行完后背伴,RCTCxxBridge會(huì)發(fā)送一個(gè)名為RCTJavaScriptDidLoadNotification的通知給RCTRootView執(zhí)行runApplication相關(guān)的邏輯。RCTRootView收到通知后會(huì)把運(yùn)行JS所需的moduleName峰髓、methodName傻寂、appKey和頁(yè)面所需的參數(shù)傳給JS。moduleName通常是@"AppRegistry"携兵,用于調(diào)用RN的名字為"AppRegistry"的組件疾掰。methodName通常是@"runApplication",用于調(diào)用JS的AppRegistry.runApplication方法徐紧。appKey是頁(yè)面的key静檬,也是AppRegistry.runApplication的入?yún)ⅲ糜谖ㄒ粯?biāo)識(shí)一個(gè)組件并级,本文中是@"NewProject"拂檩。參數(shù)很好理解了,就是初始化頁(yè)面的一些業(yè)務(wù)參數(shù)嘲碧。
- 執(zhí)行runApplication相關(guān)的邏輯鏈條:RCTRootView -> RCTBridge->RCTCxxBridge->Instace->NativeToJsBridge->JSIExecutor稻励。通過(guò)以上層層調(diào)用后,最終調(diào)用到JSIExecutor的callFunction方法,如果沒(méi)有綁定JS和Native側(cè)的方法則調(diào)用bindBridge執(zhí)行bind邏輯望抽,將MessageQueue.js中定義的4個(gè)方法綁定到JSIExecutor的成員變量上(相當(dāng)于Native指針指向JS函數(shù))加矛,這4個(gè)函數(shù)是flushQueue、callFunctionReturnFlushedQueue煤篙、invokeCallbackAndReturnFlushedQueue斟览、callFunctionReturnResultAndFlushedQueue。關(guān)于bind這4個(gè)函數(shù)的邏輯在源碼執(zhí)行階段已經(jīng)講過(guò)了辑奈。
- 然后調(diào)用callFunctionReturnFlushedQueue方法獲取JS待native刷新的方法隊(duì)列苛茂,把隊(duì)列通過(guò)JSIExecutor::callNativeModules交給JSIExecutor的m_delegate進(jìn)行處理。m_delegate是一個(gè)JSToNativeBridge的實(shí)例鸠窗。專門負(fù)責(zé)處理JS call Native相關(guān)的邏輯味悄。
- 第2步中調(diào)用callFunctionReturnFlushedQueue時(shí)會(huì)把@"AppRegistry"、@"runApplication"塌鸯、appKey、arguments作為入?yún)鬟f個(gè)JS唐片,JS側(cè)收到這個(gè)消息后丙猬,就調(diào)用了JS中的AppRegistry的runApplication方法。到這里费韭,JS的入口就被調(diào)用茧球,界面就會(huì)渲染出來(lái)。JS的入口如下:
總結(jié)下來(lái)星持,React Native用iOS自帶的JavaScriptCore作為JS的解析引擎抢埋,即JS和Native的相互通信是經(jīng)過(guò)JavaScriptCore機(jī)制來(lái)進(jìn)行的。但并沒(méi)有用到JavaScriptCore提供的一些可以讓JS與OC互調(diào)的特性督暂,而是自己實(shí)現(xiàn)了一套機(jī)制揪垄,這套機(jī)制可以通用于所有JS引擎上。
在程序啟動(dòng)階段會(huì)收集所有native暴露給JS的模塊和方法逻翁。然后初始化階段會(huì)創(chuàng)建并配置JS相關(guān)的橋饥努。在執(zhí)行JS源碼階段,Native的JSIExecutor會(huì)注入nativeModuleProxy和兩個(gè)方法到JS全局變量global中八回,相當(dāng)于JS指針指向Native變量酷愧,這樣就建立了JS調(diào)用Native的能力。注入到global中的兩個(gè)方法是nativeFlushQueueImmediate和nativeCallSyncHook缠诅,將來(lái)JS側(cè)調(diào)用這兩個(gè)方法就會(huì)走到Native側(cè)的方法實(shí)現(xiàn)中溶浴。
但是JS call Native并不直接調(diào)用Native的方法,而且先放入一個(gè)JS隊(duì)列中管引,每次Native call JS時(shí)JS都會(huì)將這個(gè)隊(duì)列返回給Native士败,Native拿到這個(gè)隊(duì)列后根據(jù)隊(duì)列中的moduleID、methodID和方法參數(shù)等信息生成NSInvocation汉匙,完成Native的方法調(diào)用拱烁。這種實(shí)現(xiàn)方式避免了JS和Native的頻繁通信生蚁。
JS腳本加載并執(zhí)行完成后native會(huì)調(diào)用JS,告訴JS可以runApplication了戏自,接下頁(yè)面救護(hù)被渲染出來(lái)邦投。
至此,全篇完擅笔!
文/VV木公子(簡(jiǎn)書作者)
PS:如非特別說(shuō)明志衣,所有文章均為原創(chuàng)作品,著作權(quán)歸作者所有猛们,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)念脯,并注明出處。
如果您是iOS開發(fā)者弯淘,或者對(duì)本篇文章感興趣绿店,請(qǐng)關(guān)注本人,后續(xù)會(huì)更新更多相關(guān)文章庐橙!敬請(qǐng)期待假勿!