詳解React Native初始化和通信機(jī)制

致敬

開始準(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)層的類):

React Native關(guān)鍵類關(guān)系圖

開始

我們新建一個(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í)序圖和涉及到的主要類:

RN bridge初始化

加載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 &&params) {
  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的流程圖:

JS call 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 &&params, 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 &params) {
  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í)例。

  1. 在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)行的流码。
  2. 接下來(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ù)。

  1. 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中走净。
  2. 向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)用。
  3. 在JSIExecutor::loadApplicationScript中會(huì)調(diào)用JSCRumtime的evaluteJavaScript方法霍衫,進(jìn)而調(diào)用到JavaScriptCore的JSEvaluateScript方法執(zhí)行JS源碼候引。
  4. 在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ù)嘲碧。

  1. 執(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ò)了辑奈。
  2. 然后調(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)的邏輯味悄。
  3. 第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)期待假勿!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市态鳖,隨后出現(xiàn)的幾起案子转培,更是在濱河造成了極大的恐慌,老刑警劉巖浆竭,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浸须,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡邦泄,警方通過(guò)查閱死者的電腦和手機(jī)删窒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虎韵,“玉大人易稠,你說(shuō)我怎么就攤上這事“叮” “怎么了驶社?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)测萎。 經(jīng)常有香客問(wèn)我亡电,道長(zhǎng),這世上最難降的妖魔是什么硅瞧? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任份乒,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘或辖。我一直安慰自己瘾英,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布颂暇。 她就那樣靜靜地躺著缺谴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耳鸯。 梳的紋絲不亂的頭發(fā)上湿蛔,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天堡妒,我揣著相機(jī)與錄音呼股,去河邊找鬼。 笑死刷喜,一個(gè)胖子當(dāng)著我的面吹牛财喳,可吹牛的內(nèi)容都是我干的察迟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼耳高,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卷拘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起祝高,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎污筷,沒(méi)想到半個(gè)月后工闺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓣蛀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年陆蟆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惋增。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叠殷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诈皿,到底是詐尸還是另有隱情林束,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布稽亏,位于F島的核電站壶冒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏截歉。R本人自食惡果不足惜胖腾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咸作,春花似錦锨阿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至毫胜,卻和暖如春书斜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酵使。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工荐吉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人口渔。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓样屠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缺脉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痪欲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容