ReactNative源碼分析 - 啟動流程

1.ReactNative源碼分析 - 概述
2.ReactNative源碼分析 - JavaScriptCore C語言篇
3.ReactNative源碼分析 - 啟動流程
4.ReactNative源碼分析 - 通信機制
5.ReactNative源碼分析 - 渲染原理

  • 一、ReactNative類圖
  • 二、ReactNative啟動流程簡析
  • 三、ReactNative啟動流程詳解
    • 0.啟動流程起點
    • 1.構建Native&JS通信表層Bridge
    • 2.創建JS線程
    • 3.收集原生模塊信息,構建原生模塊實例
    • 4.構建真正執行通信任務的底層Bridge
    • 5.加載js bundle包
    • 6.執行js bundle包
  • 四、啟動流程線程管理

一、ReactNative類圖

  • ReactNative類相對較多,下圖為對應UML類圖,列出了有助于梳理主流程的主要類。ReactNative啟動流程的層次結構較深、涉及的類較多、代碼調用從OC到OC/C++混編到純C++公共層不斷深入、閱讀源碼過程中可根據下圖梳理主要類之間的關系。筆者閱讀源碼的使用(測試DEMO)中包含一些注釋,可能會有助于理解一些細節。
ReactNative類圖.jpg

二、ReactNative啟動流程簡析

  • 探究ReactNative啟動流程,可以通過斷點調試追蹤調用棧,一步步深入。啟動流程主要做了幾件事情:

    • 1.構建Native&JS通信表層Bridge;
    • 2.創建JS線程;
    • 3.收集原生模塊(<RCTBridgeModule>)信息,構建原生模塊實例;
    • 4.構建真正執行通信任務的底層Bridge;
    • 5.加載js bundle包;
    • 6.執行js bundle包;
  • ReactNative項目啟動后存主要涉及兩個線程:主線程、JS線程。以上步驟并非線性執行,涉及線程切換/等待。大部分初始化操作、原生模塊實例創建在主線程執行;底層Bridge初始化JS線程執行,js bundle包的加載/運行都在JS線程執行。

  • 下面詳細分析啟動流程,根據出場順序先介紹各個核心類的主要功能,再從代碼層面分析。

    注意:本文只分析最常規的啟動流程,即Release版本下的啟動流程,加載本地bundle整包并執行,代碼分析會有所省略,只留下主流程。可以手動打js bundle離線包并調整Xcode開發模式為Release,有需要的話可借助DEMO。

三、ReactNative啟動流程詳解

0.啟動流程起點

ReactNative代碼入口是:RCTRootView初始化函數initWithBridge: initialProperties:,用于實例化RCTRootView作為UIViewController的view。其中用傳入RCTBridge,這便是啟動流程的源頭,啟動完畢才正在執行JS模塊AppRegistry runApplication,運行組件,開始渲染。

- (instancetype)initWithBridge:(RCTBridge *)bridge
                    moduleName:(NSString *)moduleName
             initialProperties:(NSDictionary *)initialProperties
{
   if (self = [super initWithFrame:CGRectZero]) {
    _bridge = bridge;
    _moduleName = moduleName;
    _appProperties = [initialProperties copy];
    ...
    [self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];
  }
  return self;
}

- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
  ...
  [self runApplication:bridge];
  ...
}

- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @"";
  NSDictionary *appParameters = @{
    @"rootTag": _contentView.reactTag,
    @"initialProps": _appProperties ?: @{},
  };
  
  // (Native調用JS) 調用JS模塊AppRegistry runApplication,運行組件
  [bridge enqueueJSCall:@"AppRegistry"
                 method:@"runApplication"
                   args:@[moduleName, appParameters]
             completion:NULL];
}

1.構建Native&JS通信表層Bridge

  • *RCTBridge是ReactNative暴露出來給用戶使用的類,從接口可看出它對外主要負責:
    • Bridge初始化;
    • Native調用JS:即原生調用JS模塊;
    • 啟動流程進度通知;
      實際上它內部創建并持有RCTCxxBridge,然后大部分操作是委托批量橋batchedBridge執行,即RCTCxxBridge才是真正的執行者。每個原生模塊都會弱引用它,用于隨時獲取Bridge支持。
// RCTBridge.h

// 啟動流程通知
RCT_EXTERN NSString *const RCTJavaScriptWillStartLoadingNotification;
RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification;
...
// 初始化
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
                   launchOptions:(NSDictionary *)launchOptions;
...
// 調用JS模塊函數
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion
// RCTBridge.m
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
                       bundleURL:(NSURL *)bundleURL
                  moduleProvider:(RCTBridgeModuleListProvider)block
                   launchOptions:(NSDictionary *)launchOptions
{
  if (self = [super init]) {
    _delegate = delegate;
    _bundleURL = bundleURL;
    _moduleProvider = block;
    _launchOptions = [launchOptions copy];
    
    [self setUp]; // 初始化batchedBridge
  }
  return self;
}

- (void)setUp
{
  Class bridgeClass = self.bridgeClass;
  _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
    ...
  // 構建batchedBridge(C++ Bridge)
  self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
  [self.batchedBridge start];
}

  • *RCTCxxBridge(即C++Bridge、也稱batchedBridge)是ReactNative啟動流程的主要管理者,啟動流程主要步驟、線程管理都是由它控制。主要負責:
    • 創建并持有JS線程:jsThread;
    • 收集并持有所有原生模塊(RCTBridgeModule)信息,構建原生模塊實例;
    • 創建并持有真正執行通信任務的底層Instance以及其他一系列底層模塊;
    • 加載js bundle包;
    • 等待一切就緒,執行js bundle包;

RCTCxxBridge初始化主要是創建一些標識位、原生模塊容器等。接著執行start操作,這是啟動流程的最為核心的地方,啟動流程所有操作都從這里開始。

// RCTCxxBridge.mm
- (void)start
{
  // 2.創建常駐線程,開啟runloop
  _jsThread = [[NSThread alloc] initWithTarget:[self class]
                                      selector:@selector(runRunLoop)
                                        object:nil];
  _jsThread.name = RCTJSThreadName;
  _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
  [_jsThread start];

  dispatch_group_t prepareBridge = dispatch_group_create();

  // 3.加載自動注冊的原生模塊(創建原生模塊描述對象、構造原生模塊實例)
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
 
  // 4.js線程中初始化底層 _reactInstance 
  // 創建底層 _reactInstance實例
  _reactInstance.reset(new Instance);

  std::shared_ptr<JSExecutorFactory> executorFactory;
  executorFactory = std::make_shared<JSCExecutorFactory>(nullptr); // 生產環境工廠類 JSCExecutorFactory

  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    // 主要初始化操作在initializeBridge中完成
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];

  // 5.加載 js bundle包(整包,異步加載)
  dispatch_group_enter(prepareBridge);
  __block NSData *sourceCode;
  [self loadSource:^(NSError *error, RCTSource *source) {
    if (error) {
      [weakSelf handleError:error];
    }
    sourceCode = source.data;
    dispatch_group_leave(prepareBridge);
  } onProgress:^(RCTLoadingProgress *progressData) { ... }];

  // 6.等待 需要在主線程創建的原生模塊實例創建完畢、底層_reactInstance初始化完畢、 js bundle包加載完畢,執行js bundle
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
    RCTCxxBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      [strongSelf executeSourceCode:sourceCode sync:NO];
    }
  });
}

2.創建JS線程

JS線程:這是一個常駐線程,會一直存活,涉及RunLoop線程保活相關知識。啟動過程中的很多初始化操作、Native&JS通信、執行js bundle包都在該線程進行。

// RCTCxxBridge.mm
- (void)start
{
  // 2.創建常駐線程,開啟runloop
  _jsThread = [[NSThread alloc] initWithTarget:[self class]
                                      selector:@selector(runRunLoop)
                                        object:nil];
  _jsThread.name = RCTJSThreadName;
  _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
  [_jsThread start];
  ...
 }
 
 + (void)runRunLoop
{
  @autoreleasepool {
    pthread_setname_np([NSThread currentThread].name.UTF8String);

    // 添加事件源Source,避免退出RunLoop
    CFRunLoopSourceContext noSpinCtx = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
    CFRunLoopSourceRef noSpinSource = CFRunLoopSourceCreate(NULL, 0, &noSpinCtx);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), noSpinSource, kCFRunLoopDefaultMode);
    CFRelease(noSpinSource);

    // kCFRunLoopDefaultMode模式下啟動RunLoop,永不退出
    while (kCFRunLoopRunStopped != CFRunLoopRunInMode(kCFRunLoopDefaultMode, ((NSDate *)[NSDate distantFuture]).timeIntervalSinceReferenceDate, NO)) {
      RCTAssert(NO, @"not reached assertion"); // runloop spun. that's bad.
    }
  }
}
  • ensureOnJavaScriptThread把任務派發到JS線程執行
// RCTCxxBridge.mm
- (void)ensureOnJavaScriptThread:(dispatch_block_t)block
{
  if ([NSThread currentThread] == _jsThread) {
    [self _tryAndHandleError:block];
  } else {
    [self performSelector:@selector(_tryAndHandleError:)
          onThread:_jsThread
          withObject:block
          waitUntilDone:NO];
  }
}

3.收集原生模塊信息,構建原生模塊實例

  • 小技巧:
    借助Xcode的預編譯操作來做宏替換,助于理解可讀性較差的宏。操作流程是:選中想要執行預編譯的文件,Xcode => Product => Perform Action => Preprocess “XXX.m”。文件過大可能無法轉化
預編譯操作流程截圖.jpg
  • 原生模塊的導出原理
    分析原生模塊收集流程之前,先梳理原生模塊的導出原理。導出到JS端使用的原生模塊都遵守RCTBridgeModule協議,通常通過宏RCT_EXPORT_MODULE來導出。
RCT_EXPORT_MODULE(TestManager) 

// 預處理后代碼如下
extern void RCTRegisterModule(Class); 
+ (NSString *)moduleName {
    return @"TestManager";
}
+ (void)load {
    RCTRegisterModule(self);
}

RCT_EXPORT_MODULE宏替換結果如上,主要是:實現RCTBridgeModule協議函數+ (NSString *)moduleName返回原生模塊名稱,并重寫+ (void)load函數,調用RCTRegisterModule()把類注冊到原生模塊類集合。通過函數RCTGetModuleClasses()可獲取

// RCTBridge.m
// 原生模塊表
static NSMutableArray<Class> *RCTModuleClasses;
// 原生模塊表操作隊列:保證讀寫安全(讀寫鎖:多讀一寫,不同時讀寫)
static dispatch_queue_t RCTModuleClassesSyncQueue;
NSArray<Class> *RCTGetModuleClasses(void)
{
  __block NSArray<Class> *result;
  dispatch_sync(RCTModuleClassesSyncQueue, ^{
    result = [RCTModuleClasses copy];
  });
  return result;
}

void RCTRegisterModule(Class moduleClass)
{
  ...
  dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
    [RCTModuleClasses addObject:moduleClass];
  });
}

原生模塊函數通過宏RCT_EXPORT_METHOD來導出,宏替換效果如下。可知該宏主要作用是:生成導出函數,并且生成一個以__rct_export__為前綴,加上當前行號列號作為函數名的類函數,該函數返回的結構體RCTMethodInfo包含了導出函數信息有 JS名、原生函數名、是否為同步函數。后序收集原生模塊信息正是通過runtime掃描帶__rct_export__前綴的函數,執行函數調用以獲取導出函數信息。

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSNumber *)secondsSinceUnixEpoch)
{
  NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
  RCTLogInfo(@"Date : %@",date);
}

// 上述導出函數宏替換后如下
 + (const RCTMethodInfo *)__rct_export__390 {
   static RCTMethodInfo config = {
      "",
      "addEvent:(NSString *)name location:(NSString *)location date:(NSNumber *)secondsSinceUnixEpoch",
      NO,
   };
   return &config;
 }
 - (void)addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)secondsSinceUnixEpoch ;
 {
    NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
    RCTLogInfo(@"Date : %@",date);
 }
  • RCTModuleData是原生模塊描述對象,包含原生模塊的一切信息,包括原生模塊名稱、導出函數、導出常量、函數執行線程等,原生模塊對象的實例化也由它管理。基于懶加載機制,每個原生模塊都是一個單例,只會創建一次。

初始化:RCTModuleData初始化僅需入原生模塊類型moduleClass、bridge,但已足夠構建原生模塊實例并獲取它的一切信息

// RCTModuleData.mm
- (instancetype)initWithModuleClass:(Class)moduleClass
                             bridge:(RCTBridge *)bridge
{
  // 初始化 moduleProvider,用于后面構建模塊實例_instance
  return [self initWithModuleClass:moduleClass
                    moduleProvider:^id<RCTBridgeModule>{ return [moduleClass new]; }
                    bridge:bridge];
}

構建原生模塊實例:使用c++互斥鎖std::mutex保證實例的線程安全。unique_lock()函數,獨占or等待互斥鎖_instanceLock,當lock作用域結束時,自動釋放鎖。 {...} 為加鎖區,作用是減小加鎖區域。

// RCTModuleData.mm
- (void)setUpInstanceAndBridge {
  {  // 加鎖區域
    std::unique_lock<std::mutex> lock(_instanceLock);
    if (!_setupComplete && _bridge.valid) {
      if (!_instance) {
        // 構建實例
        _instance = _moduleProvider ? _moduleProvider() : nil;
      }
      ....
      [self setBridgeForInstance];
    }
    [self setUpMethodQueue];
  }
  ...
}

// 配置原生模塊實例bridge RCTCxxBridge
- (void)setBridgeForInstance
{
  if ([_instance respondsToSelector:@selector(bridge)] && _instance.bridge != _bridge) {
      [(id)_instance setValue:_bridge forKey:@"bridge"];
  }
}

獲取導出函數:Runtime遍歷原生模塊類函數,找出以__rct_export__為前綴的并調用,以獲取導出函數信息RCTMethodInfo,構造RCTBridgeMethod實例。RCTBridgeMethod實例是原生模塊導出函數描述對象,包含導出函數信息,JS call Native最終由它來執行函數調用。

// RCTModuleData.mm
// 獲取模塊導出的方法
- (NSArray<id<RCTBridgeMethod>> *)methods
{
  if (!_methods) {
    NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];

    // Runtime遍歷原生模塊類方法,找出以__rct_export__為淺醉的類方法
    unsigned int methodCount;
    Class cls = _moduleClass;
    while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
      Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
      for (unsigned int i = 0; i < methodCount; i++) {
        Method method = methods[i];
        SEL selector = method_getName(method);
        if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
          // 執行原生函數,獲取導出函數信息
          IMP imp = method_getImplementation(method);
          auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_moduleClass, selector);
          id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod moduleClass:_moduleClass];
          [moduleMethods addObject:moduleMethod];
        }
      }

      free(methods);
      cls = class_getSuperclass(cls);
    }
    _methods = [moduleMethods copy];
  }
  return _methods;
}
  • 收集原生模塊信息,構建原生模塊實例
    回到正題,繼續流程分析。原生模塊導出到Bridge有多種方式,此處分析通過自動注冊導出(通過RCT_EXPORT_MODULE導出)的原生模塊。原生模塊導出包括兩個步驟:
    • 1.通過RCTGetModuleClasses()函數獲取原生模塊集合,并創建模原生塊描述對象集合<RCTModuleData>。在主線程執行。
    • 2.實例化(instance)需要在主線程Setup的原生模塊。實現相應協議requiresMainQueueSetup、有導出常量…會在主線程實例化。
// RCTCxxBridge.mm
- (void)start
{
   ···
  dispatch_group_t prepareBridge = dispatch_group_create();

  // 3.加載自動注冊的原生模塊(創建原生模塊描述對象、構造原生模塊實例)

  // 加載手動導出的原生模塊
  [self registerExtraModules];
  // 加載自動注冊的原生模塊(創建原生模塊描述對象、構造原生模塊實例)
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
  // 加載調試模式所需原生模塊
  [self registerExtraLazyModules];
··· 
}

- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<id<RCTBridgeModule>> *)modules
                               withDispatchGroup:(dispatch_group_t)dispatchGroup
                                lazilyDiscovered:(BOOL)lazilyDiscovered
{
    // 創建原生模塊描述對象
    NSArray<RCTModuleData *> *moduleDataById = [self _registerModulesForClasses:modules lazilyDiscovered:lazilyDiscovered];
    ...
    // 創建 自動注冊的原生模塊實例
    [self _prepareModulesWithDispatchGroup:dispatchGroup];
}

- (NSArray<RCTModuleData *> *)_registerModulesForClasses:(NSArray<Class> *)moduleClasses
                                        lazilyDiscovered:(BOOL)lazilyDiscovered
{
  NSArray *moduleClassesCopy = [moduleClasses copy];
  NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
  for (Class moduleClass in moduleClassesCopy) {
    ···
    // 創建原生模塊描述對象,并存放到對應容器
    NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
    moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
    _moduleDataByName[moduleName] = moduleData;
    [_moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
  }
  [_moduleDataByID addObjectsFromArray:moduleDataByID];
}

- (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
  for (RCTModuleData *moduleData in _moduleDataByID) {
    if (moduleData.requiresMainQueueSetup) {
      dispatch_block_t block = ^{
        if (self.valid && ![moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
          // 創建模塊實例
          (void)[moduleData instance];
          // 收集模塊導出常量
          [moduleData gatherConstants];
        }
      };

      if (RCTIsMainQueue()) {
        block();
      } else {
        if (dispatchGroup) {
          dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
        }
      }
    }
  }
}

注:觸發原生模塊實例化的線程無法保證。如果是在JS線程觸發需要在主線程實例化的原生模塊實例化,除非使用dispatch_sync同步函數派發實例化任務到主線程,否則不可以懶加載,但這種方式可能造成死鎖(假設實例化任務在主線程發起…)。為此ReactNative使用GCD隊列組,在加載js bundle的同時,在主線程構建原生模塊實例,保證在執行js bundle之前,需要在主線程實例化的原生模塊實例化完畢。

15760766355461.jpg

4.構建真正執行通信任務的底層Bridge

  • 這個步驟有一匹布那么長,會構建出UML圖中Instance以下的整個類體系,閱讀源碼需要一點點耐心,建議參照UML類圖梳理各個類之間的關系。整個流程都在JS線程執行。
    理解底層Bridge要抓住三個關鍵職能
    • 1.JS call Native
    • 2.Native call JS
    • 3.執行js bundle
// RCTCxxBridge.mm
- (void)start
{
  ...
  // 4.在js線程中初始化底層 _reactInstance 
  
  // 創建底層 _reactInstance實例,主要初始化操作在是函數initializeBridge中完成
  _reactInstance.reset(new Instance);

  std::shared_ptr<JSExecutorFactory> executorFactory;
  executorFactory = std::make_shared<JSCExecutorFactory>(nullptr); // 生產環境工廠類 JSCExecutorFactory
    
  // JS線程 初始化底層Bridge
  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];
  ...
}

- (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
  // 構建 _jsMessageThread
  __weak RCTCxxBridge *weakSelf = self;
  _jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) { ... });

  if (_reactInstance) {
    [self _initializeBridgeLocked:executorFactory];
  }
}

- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
  std::lock_guard<std::mutex> guard(_moduleRegistryLock);
  _reactInstance->initializeBridge(
                                   std::make_unique<RCTInstanceCallback>(self),
                                   executorFactory,
                                   _jsMessageThread,
                                   [self _buildModuleRegistryUnlocked]);
}

- (std::shared_ptr<ModuleRegistry>)_buildModuleRegistryUnlocked
{
  ModuleRegistry::ModuleNotFoundCallback moduleNotFoundCallback = ...
  auto registry = std::make_shared<ModuleRegistry>(
         createNativeModules(_moduleDataByID, self, _reactInstance),
         moduleNotFoundCallback);
  return registry;
}

Instance初始化函數initializeBridge,意味著已經到了ReactNative框架的公共層(提及框架的分層設計,ReactCommon目錄為iOS/Android兩端公共層),代碼為純C++。此處傳參出現了幾個新類型,在此一一解釋


  • RCTInstanceCallback實現公共層C++接口InstanceCallback,用于獲取底層回調,最終通過RCTCxxBridge派發給原生模塊(主要用于通知RCTUIManager觸發渲染)。
struct RCTInstanceCallback : public InstanceCallback {
  void onBatchComplete() override {
    [bridge_ partialBatchDidFlush];
    [bridge_ batchDidComplete];
  }
};

  • RCTMessageThread實現公共層C++接口MessageQueueThread,它是對JS線程對應的RunLoop的封裝,傳遞給底層用于底層派發任務到JS線程,支持同步/異步操作,這里使用信號量來實現同步。
// RCTMessageThread.mm

// 往runloop中添加block任務
void RCTMessageThread::runAsync(std::function<void()> func) {
  CFRunLoopPerformBlock(m_cfRunLoop, kCFRunLoopCommonModes, ^{ func(); });
  CFRunLoopWakeUp(m_cfRunLoop);
}

// 往runloop中添加block任務(使用信號量 實現同步)
void RCTMessageThread::runSync(std::function<void()> func) {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  runAsync([func=std::make_shared<std::function<void()>>(std::move(func)), &sema] {
    (*func)();
    dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

  • JSCExecutorFactory實現公共層C++接口JSExecutorFactory,使用工廠模式,在不同場景構建出不同的JSExecutor,其中生產環境使用JSCExecutorFactory,用于構建JSIExecutor。
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
  std::shared_ptr<ExecutorDelegate> delegate,
  std::shared_ptr<MessageQueueThread> jsQueue) {
  
  return folly::make_unique<JSIExecutor>(
    facebook::jsc::makeJSCRuntime(),  // 創建JS Runtime
    delegate,
    [](const std::string &message, unsigned int logLevel) { ... },
    JSIExecutor::defaultTimeoutInvoker,
    std::move(runtimeInstaller_));
}

  • RCTNativeModule實現公共層C++接口NativeModuleNativeModule定義了一套接口用于獲取原生模塊信息、調用原生模塊函數。RCTNativeModule實際上就是個C++版本的元生模塊描述類,它是對OC版本的RCTModuleData封裝。
// NativeModule.h

class NativeModule {
 public:
  virtual ~NativeModule() {}
  // 獲取模塊信息
  virtual std::string getName() = 0;
  virtual std::vector<MethodDescriptor> getMethods() = 0;
  virtual folly::dynamic getConstants() = 0;
  // 調用原生模塊函數
  virtual void invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) = 0;
  virtual MethodCallResult callSerializableNativeHook(unsigned int reactMethodId, folly::dynamic&& args) = 0;
};
// RCTCxxUtils.mm
// 構建C++原生模塊描述對象RCTNativeModule集合
std::vector<std::unique_ptr<NativeModule>> createNativeModules(NSArray<RCTModuleData *> *modules, RCTBridge *bridge, const std::shared_ptr<Instance> &instance)
{
  std::vector<std::unique_ptr<NativeModule>> nativeModules;
  for (RCTModuleData *moduleData in modules) {
    ...
    nativeModules.emplace_back(std::make_unique<RCTNativeModule>(bridge, moduleData));
  }
  return nativeModules;
}
  • Instance其實就是NativeToJsBridge的一層包裝,它主要負責創建NativeToJsBridge,并用它來調用JS模塊函數、執行JS回調(原生模塊導出函數支持帶回調類型的參數RCTResponseSenderBlock,對應的JS回調會暫存在JS端,最終再由原生端調用才得以執行)、執行JS bundle包。
void Instance::initializeBridge(
    std::unique_ptr<InstanceCallback> callback,
    std::shared_ptr<JSExecutorFactory> jsef,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<ModuleRegistry> moduleRegistry) {
  callback_ = std::move(callback);
  moduleRegistry_ = std::move(moduleRegistry);
  
  jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
    // js線程中 同步 初始化NativeToJsBridge
    nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
        jsef.get(), moduleRegistry_, jsQueue, callback_);
    
    // 同步信號,初始化 NativeToJsBridge完畢,才允許loadApplicationSync
    std::lock_guard<std::mutex> lock(m_syncMutex);
    m_syncReady = true;
    m_syncCV.notify_all();
  });
}

// 執行JS bundle
void Instance::loadApplication(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
                               std::unique_ptr<const JSBigString> string,
                               std::string sourceURL) {
  nativeToJsBridge_->loadApplication(std::move(bundleRegistry), std::move(string),
                                     std::move(sourceURL));
}

// 調用JS模塊函數
void Instance::callJSFunction(std::string &&module, std::string &&method,
                              folly::dynamic &&params) {
  nativeToJsBridge_->callFunction(std::move(module), std::move(method),
                                  std::move(params));
}
// 執行JS回調
void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &&params) {
  nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
}
  • 題外話
    上述分析可發現很多類都是對公共層C++接口的實現,ReactNative架構分層設計,底層是iOS/Andriod共用的公共層,純C++編寫。這么設計筆者的理解是:
    • 1.跨平臺的需要:Objective-C和JAVA總需要一個底層來銜接,跨平臺并且面向對象的C++是不二之選;
    • 2.把更多的處理邏輯下沉到C++層:這有助于提高代碼復用率、提升性能。
      當需要iOS/Android平臺各自實現相應功能時,采用面向協議編程思想,在公共層定義接口標準,由兩端分別實現,最終實現了分層設計的銜接。

  • ModuleRegistry是原生模塊注冊機,它持有所有原生模塊信息(NativeModule集合),負責兩個非常核心的任務:
    • 生成中間版本的原生模塊配置信息,進一步加工就可以最終導入JS端
    • 作為JS call Native的中轉站(JS&Native通信的調用鏈條比較長,詳見通信機制篇)。
// ModuleRegistry.h

ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules, ModuleNotFoundCallback callback = nullptr);

// 生成原生模塊配置信息,用于導入JS端
folly::Optional<ModuleConfig> getConfig(const std::string& name);

// 調用用原生模塊函數 moduleId:模塊索引,methodId:函數索引,params 參數
void callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId);
// 調用原生模塊同步函數
MethodCallResult callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
  
private:
// C++版本的元原生模塊集合 RCTNativeModule
std::vector<std::unique_ptr<NativeModule>> modules_;

  • NativeToJsBridge管理Native call JS,即原生模塊調用JS模塊的管理者,主要功能是
    • 創建并持有JsToNativeBridge,用于管理JS call Native(該對象最終傳遞給JSIExecutor)。
    • 使用JSExecutorFactory創建并持有JSIExecutor,用于管理 Native call JS、執行js bundle。
      所以NativeToJsBridge的命名有點迷惑,其實它的主要工作是集成,通過JSIExecutor、JsToNativeBridge來實現三個關鍵職能。
// NativeToJsBridge.cpp

// 初始化,構建JsToNativeBridge、JSIExecutor
NativeToJsBridge::NativeToJsBridge(
    JSExecutorFactory* jsExecutorFactory,
    std::shared_ptr<ModuleRegistry> registry,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<InstanceCallback> callback)
    : m_destroyed(std::make_shared<bool>(false))
    , m_delegate(std::make_shared<JsToNativeBridge>(registry, callback))
    , m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue))
    , m_executorMessageQueueThread(std::move(jsQueue)) {}
    
//  執行js bundle
void NativeToJsBridge::loadApplication(
    std::unique_ptr<RAMBundleRegistry> bundleRegistry,
    std::unique_ptr<const JSBigString> startupScript,
    std::string startupScriptSourceURL) {
    ....
}

// 調用JS模塊函數 
void NativeToJsBridge::callFunction(
    std::string&& module,
    std::string&& method,
    folly::dynamic&& arguments) { 
    ... 
}
// 執行JS回調
void NativeToJsBridge::invokeCallback(double callbackId, folly::dynamic&& arguments) { ... }

  • JsToNativeBridge,實現ExecutorDelegate接口。顧名思義用來管理JS call Native,底層最終是通過原生模塊注冊機ModuleRegistry來發起調用,因為ModuleRegistry才真正擁有所有原生模塊信息。
// NativeToJsBridge.cpp

// 調用原生模塊函數
void callNativeModules(
      JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
    // 解析函數調用元素,依次執行函數調用(JS call Native批量執行)
    for (auto& call : parseMethodCalls(std::move(calls))) {
      m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
    }
    ...
  }

// 同步調用原生模塊函數
  MethodCallResult callSerializableNativeHook(
      JSExecutor& executor, unsigned int moduleId, unsigned int methodId,
      folly::dynamic&& args) override {
    return m_registry->callSerializableNativeHook(moduleId, methodId, std::move(args));
  }

  • JSIExecutor即JS執行者,它是底層真正的集大成者,最終三個關鍵職能都是由它來管理,主要負責

    • 持有jsi::Runtime,實際實現是JSCRuntime。JSCRuntime是ReactNative對JavaScriptCore的一層面向對象的封裝,底層所有的Native&JS交互都發生在這里。
    • 管理Native call JS。
    • 持有ExecutorDelegate,實際實現是JsToNativeBridge,用于管理JS call Native。
    • 運行js bundle包。
    • 向JS端注入原生模塊信息。

    Native&JS交互的實現基于JavaScriptCore(JSCRuntime),需要Native端向JS端注入原生對象,具體實現后序會分析。

// JSIExecutor.cpp

JSIExecutor::JSIExecutor(
    std::shared_ptr<jsi::Runtime> runtime,
    std::shared_ptr<ExecutorDelegate> delegate,
    Logger logger,
    const JSIScopedTimeoutInvoker& scopedTimeoutInvoker,
    RuntimeInstaller runtimeInstaller)
    : runtime_(runtime),
      delegate_(delegate),
      nativeModules_(delegate ? delegate->getModuleRegistry() : nullptr),
      scopedTimeoutInvoker_(scopedTimeoutInvoker),
      runtimeInstaller_(runtimeInstaller) {
}

// 執行js bundle
void JSIExecutor::loadApplicationScript(
    std::unique_ptr<const JSBigString> script,
    std::string sourceURL) { ... }
    
// 調用JS模塊函數 
void JSIExecutor::callFunction(
    const std::string& moduleId,
    const std::string& methodId,
    const folly::dynamic& arguments) { ... }

// 執行JS回調
void JSIExecutor::invokeCallback(
    const double callbackId,
    const folly::dynamic& arguments) { ... }

// 調用原生模塊函數
void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {  
  // dynamicFromValue 函數調用元素 JS value 轉 c++ 動態類型
  delegate_->callNativeModules(
      *this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
}
  • 注:
    構建底層Bridge的流程相當長,出現了一堆類,目的是構建出一個分工相對明確,結構相對清晰的運行環境,用于Native&JS通信、執行js bundle包。后序文章分析完Native&JS流程、js bundle包執行流程,可以回過頭看啟動原理,應該會對這個類體系有更清晰的理解。

5.加載js bundle包

加載js bundle包只分析Release模式下本地bundle整包加載流程,對于熱更新/RAM拆包加載不做分析。這個過程相對簡單,一句話概括:異步加載沙盒中的js bundle包。

// RCTCxxBridge.mm
- (void)start
{
  ...
  // 異步加載 js bundle包
  dispatch_group_enter(prepareBridge);
  __block NSData *sourceCode;
  [self loadSource:^(NSError *error, RCTSource *source) {
    sourceCode = source.data;
    dispatch_group_leave(prepareBridge);
  } onProgress:^(RCTLoadingProgress *progressData) { ... }];
}

- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
{  
  ...
  RCTSourceLoadBlock onSourceLoad = ^(NSError *error, RCTSource *source) { 
       ...
       _onSourceLoad(error, source);
  };

  [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onProgress:onProgress onComplete:^(NSError *error, RCTSource *source) {
      onSourceLoad(error, source);
  }];
}
// RCTJavaScriptLoader.mm
+ (void)loadBundleAtURL:(NSURL *)scriptURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)onComplete
{
  ...
  if (isCannotLoadSyncError) {
    // 整包,異步加載
    attemptAsynchronousLoadOfBundleAtURL(scriptURL, onProgress, onComplete);
  } else { ... }
}

static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoadProgressBlock onProgress, RCTSourceLoadBlock onComplete)
{
  scriptURL = sanitizeURL(scriptURL);
  if (scriptURL.fileURL) {
  
 // 整包,異步加載
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSData *source = [NSData dataWithContentsOfFile:scriptURL.path
                      options:NSDataReadingMappedIfSafe
                        error:&error];
onComplete(error, RCTSourceCreate(scriptURL, source, source.length));
});
return;
}
  ...
}

6.執行js bundle包

上述所有工作完畢,意味著Native&JS通信、執行js bundle包的環境已經構建完畢,最后一個環節:執行執行js bundle包。執行流程圖如下,其實就是把js bundle包經過層層傳遞,最終交給JavaScriptCore去執行。

js bundle執行流程圖.png
// RCTCxxBridge.mm
- (void)start
{
   ...
  // 等待”原生模塊實例創建完畢、底層Bridge初始化完畢、js bundle包加載完畢“,在JS線程執行js源碼
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
    RCTCxxBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      [strongSelf executeSourceCode:sourceCode sync:NO];
    }
  });
}

- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
{
  // JS bundle包執行完畢回調
  dispatch_block_t completion = ^{
    // 執行暫存的Native call JS
    [self _flushPendingCalls];
    dispatch_async(dispatch_get_main_queue(), ^{
      // 主線程發送通知
      [[NSNotificationCenter defaultCenter]
       postNotificationName:RCTJavaScriptDidLoadNotification
       object:self->_parentBridge userInfo:@{@"bridge": self}];
      // 開啟定時任務,最終用于驅動JS端定時任務
      [self ensureOnJavaScriptThread:^{
        [self->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]];
      }];
    });
  };

  [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
}

- (void)enqueueApplicationScript:(NSData *)script
                             url:(NSURL *)url
                      onComplete:(dispatch_block_t)onComplete
{
  // 底層轉化為在JS線程執行JS bundle
  [self executeApplicationScript:script url:url async:YES];
  // JS線程執行回調(由于底層js bundle最終會在JS線程執行,因此可以保證先執行完JS bundle,后執行成功回調)
  if (onComplete) {
    _jsMessageThread->runOnQueue(onComplete);
  }
}

- (void)executeApplicationScript:(NSData *)script
                             url:(NSURL *)url
                           async:(BOOL)async
{
  [self _tryAndHandleError:^{
    NSString *sourceUrlStr = deriveSourceURL(url);
    ...
    // 整包,異步執行
    self->_reactInstance->loadScriptFromString(std::make_unique<NSDataBigString>(script),sourceUrlStr.UTF8String, !async);
  }];
}
// Instance.cpp
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
                                    std::string sourceURL,
                                    bool loadSynchronously) {
    ...
    loadApplication(nullptr, std::move(string), std::move(sourceURL));
}

void Instance::loadApplication(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
                               std::unique_ptr<const JSBigString> string,
                               std::string sourceURL) {
  nativeToJsBridge_->loadApplication(std::move(bundleRegistry), std::move(string),
                                     std::move(sourceURL));
}
// NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
    std::unique_ptr<RAMBundleRegistry> bundleRegistry,
    std::unique_ptr<const JSBigString> startupScript,
    std::string startupScriptSourceURL) {

  // 派發到JS線程執行js bundle
  runOnExecutorQueue( [...] (JSExecutor* executor) mutable {
      ...
      executor->loadApplicationScript(std::move(*startupScript), std::move(startupScriptSourceURL));
  });
}
// JSIExecutor.cpp
void JSIExecutor::loadApplicationScript(
    std::unique_ptr<const JSBigString> script,
    std::string sourceURL) {
  
  // 向JS環境全局對象global注入各種代理,用于向JS端傳遞原生模塊信息、Native&JS互調
  runtime_->global().setProperty( ... );
  ....
  
  // 執行JS腳本
  runtime_->evaluateJavaScript(std::make_unique<BigStringBuffer>(std::move(script)), sourceURL);
  
  // 執行JS異步任務,JS call Navite
  flush();
}

至此ReactNative啟動流程已經完畢,開始運行js bundle代碼,執行前面RCTRootView調用JS模塊AppRegistry runApplication,運行組件,開始頁面渲染。。。。

四、啟動流程線程管理

RCTCxxBridge.mm
- (void)start
{
  dispatch_group_t prepareBridge = dispatch_group_create();

  // 3.加載自動注冊的原生模塊
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
 
  // 4.js線程中初始化底層底層Bridge
  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];

  // 5.加載 js bundle包(整包,異步加載)
  dispatch_group_enter(prepareBridge);
  __block NSData *sourceCode;
  [self loadSource:^(NSError *error, RCTSource *source) {
    sourceCode = source.data;
    dispatch_group_leave(prepareBridge);
  } onProgress:^(RCTLoadingProgress *progressData) { ... }];

  // 6.執行js bundle 源碼
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
    RCTCxxBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      [strongSelf executeSourceCode:sourceCode sync:NO];
    }
  });
}

- (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
  for (RCTModuleData *moduleData in _moduleDataByID) {
    if (moduleData.requiresMainQueueSetup) {
      dispatch_block_t block = ^{
          // 創建模塊實例
          (void)[moduleData instance];
          [moduleData gatherConstants];
      };

      if (initializeImmediately && RCTIsMainQueue()) {
        block();
      } else {
        //  異步派發模塊實例創建任務到主隊列
        if (dispatchGroup) {
          dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
        }
      }
      _modulesInitializedOnMainQueue++;
    }
  }
}
  • 啟動流程最后一個步驟“執行js bundle 源碼”,需要等前面所有工作準備就緒才可以開始。ReactNative使用了GCD的隊列組dispatch_group_t來實現:下列步驟用一個隊列組來管理,
    • 3.創建需要在主線程創建的原生模塊實例;
    • 4.js線程中初始化底層底層Bridge;
    • 5.加載 js bundle包(整包,異步加載)
      等任務執行完畢通過dispatch_group_notify通知執行 6.執行js bundle包。簡單的線程管理使得3和4、5并發執行,并保證執行JS代碼時,底層通信Bridge和原生模塊都創建完畢。

Reference

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容