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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容