react-native框架中iOS端启动流程(超级详细)

为了进一步了解rn工作原理。近期在梳理rn项目启动流程。写篇文章记录一下。
梳理的过程。新建一个空白的rn项目。然后从iOS启动的代码深入进去,了解整个项目启动过程里都发生了什么事情。ps: 本文侧重于启动流程,未涉及到reload的流程。
代码版本:

  "react": "16.13.1",
  "react-native": "0.63.3"

整体流程简介


一. 创建bridge
核心方法: RCTCxxBridge 中的 Start

  • 1、发送js将要loading的通知
  • 2、创建js线程
  • 3、加载原生模块
  • 4、创建reactInstance实例
  • 5、创建jsExecutor
  • 6、创建nativeToJsBridge
  • 7、加载js代码
  • 8、执行js代码

二. 创建rootView

    1. 创建视图
    1. 添加监听
    1. 显示加载画面
    1. 处理js加载完毕后的逻辑

三. 添加到window上显示

结合代码梳理上述流程


首先我们看在AppDelegate.m里,(对于不太了解iOS的同学,可以任务这里是项目的入口)

- (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:@"rnNativeSourceCodeLearnDemo"
                                            initialProperties:nil];

  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  // 3.创建跟控制器
  UIViewController *rootViewController = [UIViewController new];
  // 4.将rootViewController上面的视图替换为rootView
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  // 5.window显示
  [self.window makeKeyAndVisible];
  return YES;
}

从上述代码中可以看到。整个rn启动整体上看就做了两件事情:

  • 1、创建bridge
  • 2、创建rootView

先看第一个阶段

一、创建Bridge


RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];开始,往里查看

RCTBridge.m

- (instancetype)initWithDelegate:(id)delegate launchOptions:(NSDictionary *)launchOptions
{
  return [self initWithDelegate:delegate bundleURL:nil moduleProvider:nil launchOptions:launchOptions];
}
- (instancetype)initWithDelegate:(id)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
{
  ...

  Class bridgeClass = self.bridgeClass;

  ...

  self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
  [self.batchedBridge start];

  ...
}

最终核心的逻辑都是在RCTCxxBridge的start方法里,也就是文章一开始列举的8个步骤,下面是方法全部的内容:

// RCTCxxBridge.mm
- (void)start
{
  ...
// 1.发送通知:js将要开始loading
  [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
                                                      object:_parentBridge
                                                    userInfo:@{@"bridge" : self}];

// 2、创建jsThread,(用来负责执行js代码)
  // Set up the JS thread early
  _jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
  _jsThread.name = RCTJSThreadName;
  _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
  _jsThread.stackSize *= 2;
#endif
  [_jsThread start];

  dispatch_group_t prepareBridge = dispatch_group_create();

  ...

// 3、加载原生模块
// 3.1、加载用户自定义的原生模块
  [self registerExtraModules];
  // Initialize all native modules that cannot be loaded lazily
// 3.2、加载官方定义的原生模块
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 3.3、加载调试相关的原生模块
  [self registerExtraLazyModules];

  [_performanceLogger markStopForTag:RCTPLNativeModuleInit];

// 4、创建reactInstance实例。实际啥都没做,真实操作发生在后续的initializeBridge。不太清楚这里为啥这么写,跪请了解的大佬指点迷津。
  // This doesn't really do anything.  The real work happens in initializeBridge.
  _reactInstance.reset(new Instance);

  __weak RCTCxxBridge *weakSelf = self;

// 5、创建executorFactory对象。工厂模式的使用。内部会根据不同环境创建不同的executor。包括后期fb官方想切换executor,都很方便。
  // Prepare executor factory (shared_ptr for copy into block)
  std::shared_ptr executorFactory;
  if (!self.executorClass) {
    if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
      id cxxDelegate = (id)self.delegate;
      executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
    }
    if (!executorFactory) {
      executorFactory = std::make_shared(nullptr);
    }
  } else {
    id objcExecutor = [self moduleForClass:self.executorClass];
    executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
      if (error) {
        [weakSelf handleError:error];
      }
    }));
  }

// 6、在JavaScript线程内异步执行初始化NativeToJsBridge的方法。native和js交互的实现基础,都是在此步骤内实现的
  // Dispatch the instance initialization as soon as the initial module metadata has
  // been collected (see initModules)
  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];

// 7、异步加载jsbundle代码
  // Load the source asynchronously, then store it for later execution.
  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) {
#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include()
        id loadingView = [weakSelf moduleForName:@"DevLoadingView"
                                                      lazilyLoadIfNecessary:YES];
        [loadingView updateProgress:progressData];
#endif
      }];

// 8、 执行js代码。在6、7两个异步任务都完成后,才会触发该步骤执行
  // 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];
    }
  });
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}

接下来我们拆碎了看看每一步都做了些什么,探究一下rn这个框架的实现细节。

start方法内各个步骤的的细节


1.发送通知。

此处就是发送一个JavaScript将要开始加载的通知。

...

[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
                                                      object:_parentBridge
                                                    userInfo:@{@"bridge" : self}];

...

2. 创建jsThread


...

 _jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
  _jsThread.name = RCTJSThreadName;
  _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
  _jsThread.stackSize *= 2;
#endif
  [_jsThread start];

...

创建一个js线程。后续js代码的执行都在这个线程里。除了js线程,后续还会出现一个js消息线程:jsMessageThread。这个线程处理的是native和js之间通信的。 runRunLoop的意义是线程保活。
整个rn框架里面存在三个线程:

  • 主线程(UIThread)。负责渲染工作
  • js线程(jsThread)。负责执行js代码。
  • js消息线程(jsMessageThread)。负责管理所有的native和js之间的通讯。
+ (void)runRunLoop
{
  @autoreleasepool {
    
    ...

    // copy thread name to pthread name
    pthread_setname_np([NSThread currentThread].name.UTF8String);

    // Set up a dummy runloop source to avoid spinning
    CFRunLoopSourceContext noSpinCtx = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
    CFRunLoopSourceRef noSpinSource = CFRunLoopSourceCreate(NULL, 0, &noSpinCtx);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), noSpinSource, kCFRunLoopDefaultMode);
    CFRelease(noSpinSource);

    ...

    // run the run loop
    while (kCFRunLoopRunStopped !=
           CFRunLoopRunInMode(
               kCFRunLoopDefaultMode, ((NSDate *)[NSDate distantFuture]).timeIntervalSinceReferenceDate, NO)) {
      RCTAssert(NO, @"not reached assertion"); // runloop spun. that's bad.
    }
  }
}

这个函数目的就是实现线程保活。如果开了子线程,不保活,执行完任务后,子线程就销毁了。

3、加载原生模块

该阶段内一共处理个三个事情。

  • 1、加载用户自定义的原生模块
  • 2、加载fb官方定义的原生模块
  • 3、debug环境下加载debug相关的原生模块。比如LogBox

继续看一下每个加载过程都做了什么

3.1 加载用户自定义的原生模块

[self registerExtraModules];

// RCTCxxBridge.mm
- (void)registerExtraModules
{

  ...

// 获取所有的用户自定义的模块
  NSArray> *extraModules = nil;
  if ([self.delegate respondsToSelector:@selector(extraModulesForBridge:)]) {
    extraModules = [self.delegate extraModulesForBridge:_parentBridge];
  } else if (self.moduleProvider) {
    extraModules = self.moduleProvider();
  }

 ...

  // 遍历模块
  for (id module in extraModules) {
    Class moduleClass = [module class];
    NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);

   ...

    // 创建moduleData实例对象
    RCTModuleData *moduleData = [[RCTModuleData alloc] initWithModuleInstance:module bridge:self];
    // 用字典记录已经创建的结果,在后面阶段内,再次添加其他模块的时候,如果发现在此步骤已经有同名的,则不会再次创建。可以理解为我们可以重写某些官方定义的模块
    _moduleDataByName[moduleName] = moduleData;
    [_moduleClassesByID addObject:moduleClass];
    // 这个数组记录所有的的模块信息
    [_moduleDataByID addObject:moduleData];
  }

  ...

}

1、获取所有用户自定义的模块信息
2、遍历模块,逐一创建对应的moduleData。
3、将创建的moduleData添加到_moduleDataByID数组内

看到此处可能有人会有疑问: 怎么获取到的需要加载哪些原生模块的呢?
这个问题,我们先暂且不管。就认为目前已经能拿到所有的需要导出的给js的原生模块。后续会详细讨论这个问题。

3.2 加载官方定义的原生模块

(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
RCTCxxBridge.mm

- (NSArray *)_initializeModules:(NSArray *)modules
                               withDispatchGroup:(dispatch_group_t)dispatchGroup
                                lazilyDiscovered:(BOOL)lazilyDiscovered
{
  // 类似3.1中的处理过程,为导出的模块,创建对应的moduleData,添加到数组内。
  // 最终返回moduleDataById数组。内部会记录每个模块是否需要到主线程初始化。
  // 一般来讲涉及到UIKit相关的模块,以及需要创建静态变量的模块,都会需要到主线程实例化
  NSArray *moduleDataById = [self _registerModulesForClasses:modules
                                                             lazilyDiscovered:lazilyDiscovered];

  if (lazilyDiscovered) {
// 懒加载的模块此时不需要实例化对应的对象。但是貌似目前并没有需要懒加载的。
...

  } else {

    ...
    // 遍历上一步得到的moduleData列表
    for (RCTModuleData *moduleData in _moduleDataByID) {
      if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
        // 不需要到主线程的就在当前线程实例化
        (void)[moduleData instance];
      }
    }

    ...

    // From this point on, RCTDidInitializeModuleNotification notifications will
    // be sent the first time a module is accessed.
    _moduleSetupComplete = YES;
    // 处理需要到主线程处理的模块
    [self _prepareModulesWithDispatchGroup:dispatchGroup];
  }

...

  return moduleDataById;
}
  • 1、类似3.1中的处理过程,为导出的模块创建对应的moduleData,添加到数组内。最终返回moduleDataById。内部会记录每个模块是否需要到主线程初始化。一般来讲涉及到UIKit相关的模块和需要创建静态变量的模块,都会需要到主线程实例化
  • 2、遍历上一步的moduleDataById中的moduleData。如果不需要用到主线程的,就在当前实例化
  • 3 最后处理要用到主线程的模块

看一下第一步的细节

- (NSArray *)_registerModulesForClasses:(NSArray *)moduleClasses
                                        lazilyDiscovered:(BOOL)lazilyDiscovered
{

  ...

  NSArray *moduleClassesCopy = [moduleClasses copy];
  NSMutableArray *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
  // 遍历所有的模块
  for (Class moduleClass in moduleClassesCopy) {
    if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
      continue;
    }
    NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);

    RCTModuleData *moduleData = _moduleDataByName[moduleName];
    // 如果已经存在moduleData,就continue。
    if (moduleData) {
      if (moduleData.hasInstance || lazilyDiscovered) {
        continue;
      } else if ([moduleClass new] == nil) {
        continue;
      } else if ([moduleData.moduleClass new] != nil) {
        RCTLogWarn(
            @"Attempted to register RCTBridgeModule class %@ for the "
             "name '%@', but name was already registered by class %@",
            moduleClass,
            moduleName,
            moduleData.moduleClass);
      }
    }
    // 实例化还没有moduleData的模块,
    moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];

    _moduleDataByName[moduleName] = moduleData;
    [_moduleClassesByID addObject:moduleClass];
    // 添加到本方法内部数组里
    [moduleDataByID addObject:moduleData];
  }
  // 将此阶段所有的模块moduleData合并到总数组里
  [_moduleDataByID addObjectsFromArray:moduleDataByID];

   ...

  return moduleDataByID;
}

继续看一下第三步的方法,看一下具体怎么处理需要到主线程实例化的模块

- (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{

  ...

  BOOL initializeImmediately = NO;
  if (dispatchGroup == NULL) {
    
    ...

    initializeImmediately = YES;
  }

  ...
  
  // 遍历所有的moduleData
  for (RCTModuleData *moduleData in _moduleDataByID) {
    // 如果需要到主线程设置
    if (moduleData.requiresMainQueueSetup) {
       // 创建对应的block
      dispatch_block_t block = ^{
        if (self.valid && ![moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
          ...
          (void)[moduleData instance];
          [moduleData gatherConstants];
          ...
        }
      };
      
      // 如果当前在主线程,立即执行对应的任务
      if (initializeImmediately && RCTIsMainQueue()) {
        block();
      } else {
        // 如果不是,切换到主线程执行block
        if (dispatchGroup) {
          dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
        }
      }
      _modulesInitializedOnMainQueue++;
    }
  }
  
   ...

}
  • 1、遍历所有的moduleData
  • 2、如果需要到主线程设置
  • 3、创建实例化模块的block
  • 4、 如果当前在主线程,立即执行对应的任务
  • 5、 如果不是,切换到主线程执行block

4、 创建reactInstance实例

啥都没做。比较奇怪这里为什么会这么写。跪请了解的大佬指点迷津。


...

// 官方注释:此时啥都没做,实际的工作在initializeBridge里
_reactInstance.reset(new Instance);

...

5、 创建executorFactoty.

创建executorFactory对象。这里采用的工厂模式创建了一个执行器工厂,根据不同的环境可以最终创建不同的执行器实例。再生产环境下是JSIExecutor.关于执行器暂且可以理解为这相当于js引擎,其实不对,里面虽然最终会对应一个引擎,额外的还有很多事情。只不过不耽误理解整体rn框架的逻辑。


...

__weak RCTCxxBridge *weakSelf = self;

  // Prepare executor factory (shared_ptr for copy into block)
  std::shared_ptr executorFactory;
  if (!self.executorClass) {
    if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
      id cxxDelegate = (id)self.delegate;
      executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
    }
    if (!executorFactory) {
      executorFactory = std::make_shared(nullptr);
    }
  } else {
    id objcExecutor = [self moduleForClass:self.executorClass];
    executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
      if (error) {
        [weakSelf handleError:error];
      }
    }));
  }

...

6、 创建bridge.(最核心的部分)

这个部分一步步打通了native和js的通讯桥梁。使得native和js之间可以互相通讯。这地方能回答平时面试里可能遇到的native和js怎么通讯这个问题。

bridge的构建发生在JavaScript线程里。
此处强调一下。代码里多处出现bridge的说法。rn框架内也常常有人说通过bridge管理通讯。但是在代码里bridge就多个:

  • RCTBridge。这个是最外层,是oc版本的bridge。内部包含RCTCxxBridge
  • RCTCxxBridge。c++版本的bridge。内部包含NativeToJsBridge
  • NativeToJsBridge。c++实现的。这个是管理原生端向js端通讯的bridge。内部直接可以获取到JSCRuntime(等同于引擎)。处理通讯核心逻辑的bridge。后面会做详细分析。
    此处实例化bridge就是要构建所有的bridge,并打通native和js之间的通讯和交互。
...

dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    // 上一步创建的executorFactory做参数
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];

...

上一步创建的executorFactory做参数_initializeBridge

- (void)_initializeBridge:(std::shared_ptr)executorFactory
{
  if (!self.valid) {
    return;
  }
    // 在jsThread中创建JSMessageThread。并且把currentRunloop传递进去。
    // 猜测是两个线程共用了一个runloop对象保活。能保证两个线程声明周期一致?  
    // 不太确定这么写法的目的。同样跪请有了解的大佬指点迷津
  __weak RCTCxxBridge *weakSelf = self;
  _jsMessageThread = std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) {
    if (error) {
      [weakSelf handleError:error];
    }
  });

  ...

  // This can only be false if the bridge was invalidated before startup completed
  if (_reactInstance) {

    ...
  
    // 初始化bridge
    [self _initializeBridgeLocked:executorFactory];

     ...

  }

  ...

}

在jsThread中创建JSMessageThread。并且把currentRunloop传递进去。 猜测是两个线程共用了一个runloop对象保活。能保证两个线程声明周期一致? 不太确定这么写法的目的。同样跪请有了解的大佬指点迷津。
然后走到[self _initializeBridgeLocked:executorFactory];方法。字面意思就能看明白是需要通过加锁的方式初始化bridge,确保线程安全

- (void)_initializeBridgeLocked:(std::shared_ptr)executorFactory
{
  // 加锁确保线程安全
  std::lock_guard guard(_moduleRegistryLock);

  // This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
  _reactInstance->initializeBridge(
      std::make_unique(self),
      executorFactory,
      _jsMessageThread,
      [self _buildModuleRegistryUnlocked]);
  _moduleRegistryCreated = YES;
}

介绍一下_reactInstance->initializeBridge方法的参数:

  • 初始化bridge成功后的回调
  • executorFactory。一开始创建的执行器工厂对象
  • jsMessageThread。这个是js消息线程。管理native和js之间通讯的
  • 原生模块的注册后的信息。这里并不是直接将模块信息传递进去,而是将之前加载的所有原生模块,做了一层包装。这个新对象内部能获取到所有原生模块的信息。

看一下是怎么注册原生模块的

- (std::shared_ptr)_buildModuleRegistryUnlocked
{
  if (!self.valid) {
    return {};
  }

  ...

  // 创建一个原生模块查找不到的回调
  __weak __typeof(self) weakSelf = self;
  ModuleRegistry::ModuleNotFoundCallback moduleNotFoundCallback = ^bool(const std::string &name) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    return [strongSelf.delegate respondsToSelector:@selector(bridge:didNotFindModule:)] &&
        [strongSelf.delegate bridge:strongSelf didNotFindModule:@(name.c_str())];
  };
  
  // 调用createNativeModules,获取原生模块注册管理对象registry
  auto registry = std::make_shared(
      createNativeModules(_moduleDataByID, self, _reactInstance), moduleNotFoundCallback);

  ...
  // 返回原生模块注册管理对象registry
  return registry;
}

createNativeModules(NSArray *modules, RCTBridge *bridge, const std::shared_ptr &instance)
{
  // 创建nativeModules对象
  std::vector> nativeModules;
  // 遍历所有的moduleData
  for (RCTModuleData *moduleData in modules) {
    // 如果是RCTCxxModule类的子类
    if ([moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
      // 包装成在添加进去CxxNativeModule
      nativeModules.emplace_back(std::make_unique(
          instance,
          [moduleData.name UTF8String],
          [moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; },
          std::make_shared(moduleData)));
    } else {
      // // 如果不是RCTCxxModule类的子类,包装成RCTNativeModule添加进去
      nativeModules.emplace_back(std::make_unique(bridge, moduleData));
    }
  }
  // 最终返回原生模块注册表
  return nativeModules;
}

这里整体概况一下,就是将之前生成的原生模块的配置信息添加到一个注册表里。然后把这个配置表对象,作为下一步的参数。

我们可以看到native模块是分为c++模块和oc模块两类的

最后看一下CxxNativeModule和RCTNativeModule的结构

class RN_EXPORT CxxNativeModule : public NativeModule {
 public:
  CxxNativeModule(
      std::weak_ptr instance,
      std::string name,
      xplat::module::CxxModule::Provider provider,
      std::shared_ptr messageQueueThread)
      : instance_(instance),
        name_(std::move(name)),
        provider_(provider),
        messageQueueThread_(messageQueueThread) {}

  std::string getName() override;
  std::vector getMethods() override;
  folly::dynamic getConstants() override;
  void invoke(unsigned int reactMethodId, folly::dynamic &¶ms, int callId)
      override;
  MethodCallResult callSerializableNativeHook(
      unsigned int hookId,
      folly::dynamic &&args) override;

 private:
  void lazyInit();

  std::weak_ptr instance_;
  std::string name_;
  xplat::module::CxxModule::Provider provider_;
  std::shared_ptr messageQueueThread_;
  std::unique_ptr module_;
  std::vector methods_;
};

// ------------------------------------我是一条分割线---------------------------------------------

class RCTNativeModule : public NativeModule {
 public:
  RCTNativeModule(RCTBridge *bridge, RCTModuleData *moduleData);

  std::string getName() override;
  std::vector getMethods() override;
  folly::dynamic getConstants() override;
  void invoke(unsigned int methodId, folly::dynamic &¶ms, int callId)
      override;
  MethodCallResult callSerializableNativeHook(
      unsigned int reactMethodId,
      folly::dynamic &¶ms) override;

 private:
  __weak RCTBridge *m_bridge;
  RCTModuleData *m_moduleData;
};

都会有这么几个方法

  • 获取模块名:
    std::string getName() override:

  • 获取模块里的方法列表
    std::vector getMethods() override

  • 获取模块里的常量
    folly::dynamic getConstants() override;:

  • 执行某函数
    void invoke(unsigned int reactMethodId, folly::dynamic &¶ms, int callId) override;

  • 序列化
    MethodCallResult callSerializableNativeHook( unsigned int hookId, folly::dynamic &&args) override;:

这里先看到这里我们回过头来继续看bridge的初始化

  • 成功后的回调
  • executorFactory。执行器工厂对象
  • jsMessageThread。js消息线程。
  • 原生模块的注册后的信息。
- (void)_initializeBridgeLocked:(std::shared_ptr)executorFactory
{
  std::lock_guard guard(_moduleRegistryLock);

  // This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
  _reactInstance->initializeBridge(
      std::make_unique(self),
      executorFactory,
      _jsMessageThread,
      [self _buildModuleRegistryUnlocked]);
  _moduleRegistryCreated = YES;
}

initializeBridgeInstance.cpp中,是个c++的方法

// Instance.cpp
void Instance::initializeBridge(
    std::unique_ptr callback,
    std::shared_ptr jsef,
    std::shared_ptr jsQueue,
    std::shared_ptr moduleRegistry) {
  callback_ = std::move(callback);
  moduleRegistry_ = std::move(moduleRegistry);
  // 往jsQueue里添加一个任务。此处的jsQueue对应的线程是外部传进来的jsMessageThread。不是jsThread。
  jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
    // 任务的内容
    // 1、创建nativeToJsBridge_实例
    nativeToJsBridge_ = std::make_shared(
        jsef.get(), moduleRegistry_, jsQueue, callback_);
    // 2、调用nativeToJsBridge_实例中初始化runtime的方法
    nativeToJsBridge_->initializeRuntime();

    // 3、将刚创建的nativeToJsBridge_设置给m_nativeToJsBridge记录下来。
    // 然后清空对js的调用的任务队列,清空是指取出来执行完。
    jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);

    std::lock_guard lock(m_syncMutex);
    m_syncReady = true;
    m_syncCV.notify_all();
  });

  ...

}

这个方法的本质是往js消息队列里添加了一个任务。后续在执行队列任务的时候,再取出来执行。
我们看一下这个任务的内容:

  • 1、创建nativeToJsBridge_实例
  • 2、调用nativeToJsBridge_实例中初始化runtime的方法
  • 3、将刚创建的nativeToJsBridge_设置给m_nativeToJsBridge记录下来。然后清空对js的调用的任务队列,清空是指取出来执行完。
    本身是一个要入队的任务。这个任务里,最后还会触发一次取出来任务继续执行的操作。猜测一下:应该是因为在实际执行initializeRuntime是,可能队列里又加入别的任务,也要执行这些任务。

继续跟下去。看看初始化runtime做了啥:

// NativeToJsBridge.cpp
void NativeToJsBridge::initializeRuntime() {
  // 往执行队列里添加一个任务
  runOnExecutorQueue(
      // 任务本身是调用executor的初始化runtime
      [](JSExecutor *executor) mutable { executor->initializeRuntime(); });
}

这个方法本质也是创建一个任务,添加到执行队列里。
任务的内容是调用executor的初始化runtime方法

继续跟踪,看一下最终的初始化runtime发生了什么。 在JSIExecutor.cpp

// JSIExecutor.cpp
void JSIExecutor::initializeRuntime() {
  SystraceSection s("JSIExecutor::initializeRuntime");
  // 1、在jscruntime中获取全局对象,设置一个nativeModuleProxy属性,value是一个NativeModuleProxy类型的对象
  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared(nativeModules_)));

  // 2、在jscruntime中获取全局对象,设置一个nativeFlushQueueImmediate属性,value是一个createFromHostFunction函数
  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) {
            if (count != 1) {
              throw std::invalid_argument(
                  "nativeFlushQueueImmediate arg count must be 1");
            }
            callNativeModules(args[0], false);
            return Value::undefined();
          }));
  
  // 3、在jscruntime中获取全局对象,设置一个nativeCallSyncHook属性,value是一个createFromHostFunction函数
  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); }));

      ...

  if (runtimeInstaller_) {
    runtimeInstaller_(*runtime_);
  }
  
   ...

}

敲黑板,全局核心。

native端获取到runtime实例。可以认为是js的执行环境。在这个环境的global对象上,添加了几个属性并赋值。这个global对象,在js的执行上下文中可以直接获取到。就是js里的global对象。global上的属性也能直接在js端获取到,并且直接使用。
这个地方是native和js之间直接交互的部分,也是native和js可以互相通讯的核心所在。
整个bridge的构建核心就是完成这个global对象的构建。

整个global对象类似下面的结构

    global = {
        nativeModuleProxy: NativeModuleProxy,
        nativeFlushQueueImmediate: (flushQueue) => { callNativeModules(flushQueue) },
        nativeCallSyncHook: (args) => {return nativeCallSyncHook(args, count)}
    }

概括的介绍一下这几个属性:

  • nativeModuleProxy: 简单来说是原生模块的配置信息。本质上是对原生模块配置表的一个包装。内部可以通过moduleID,methodID可以查找到对应的原生模块和模块方法。
  • nativeFlushQueueImmediate: 这个是一个函数,可以认为是js端负责调用。native负责实现。这个是js端唯二主动调用native的其中一个地方。
  • nativeCallSyncHook: 本质是一个函数。js端负责调用,native端负责实现。当js端发起一个对原生模块的同步调用时,会在js端调用此方法。到达native端后,方法内会根据传递过来的moduleID和methodID查找到对应的原生模块的原生方法,执行完毕后,将结果返回给js端。这是另一个js端会直接调用native的地方。

自此native和js通讯的bridge基本构造完毕。实际native和js端能通信的的就是上述这个global对象。
为啥说是基本构造完毕,因为除了native端给global设置属性外,js端也会给这个global设置属性。后文会提到,此处先做个简介

    global = {
        nativeModuleProxy: NativeModuleProxy,
        nativeFlushQueueImmediate: (flushQueue) => { callNativeModules(flushQueue) },
        nativeCallSyncHook: (args) => {return nativeCallSyncHook(args, count)},
        __fbBatchedBridge: BatchedBridge, // js端添加的
        __fbGenNativeModule: function genModule(
                                 config: ?ModuleConfig,
                                 moduleID: number,
                              ): ?{
                                 name: string,
                                 module?: Object,
                                 ...
                              }, // js添加
    }

为了加深印象和便于理解,详细介绍一下这几个属性

  • NativeModuleProxy: 负责管理所有的原生模块的注册信息。到了js端会对应rn开发中使用的NativeModules。是同一个对象。
    当在js端使用原生模块的时候:
import {NativeModules} from 'react-native';
const CalendarManager = NativeModules.CalendarManager;
export default CalendarManager;

NativeModules.CalendarManager,会调用到原生的NativeModuleProxy实例里的get方法

// JSIExecutor.app

Value get(Runtime &rt, const PropNameID &name) override {
    if (name.utf8(rt) == "name") {
      return jsi::String::createFromAscii(rt, "NativeModules");
    }
    
    // 获取原生模块注册信息
    auto nativeModules = weakNativeModules_.lock();
    // 如果不存在返回空
    if (!nativeModules) {
      return nullptr;
    }
    // 如果存在返回getModule执行后的结果
    return nativeModules->getModule(rt, name);
  }

NativeModuleProxy会根据传递过来的moduleName获取native端对应的模块注册信息,并返回给js端。

继续看看getModule做了什么

// JSINativeModules.cpp
Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name) {
  // 如果原生模块的注册表对象不存在,返回空
  if (!m_moduleRegistry) {
    return nullptr;
  }

  // 获取要获取的模块名称
  std::string moduleName = name.utf8(rt);
  
  //  查询缓存,如果缓存里找到了,且不是最后一个,返回当前对象的second。
  const auto it = m_objects.find(moduleName);
  if (it != m_objects.end()) {
    return Value(rt, it->second);
  }
  
  //   如果缓存里没有找到,则创建module
  auto module = createModule(rt, moduleName);
  // 如果创建的模块是没有信息的,返回空
  if (!module.hasValue()) {
    // Allow lookup to continue in the objects own properties, which allows for
    // overrides of NativeModules
    return nullptr;
  }
  // 添加到缓存里,返回当前的second。具体为啥这么设计,不太明白。跪求了解的大佬指点
  auto result =
      m_objects.emplace(std::move(moduleName), std::move(*module)).first;
  return Value(rt, result->second);
}
  • 1、查看原生模块注册表存不存在,不存在直接返回空
  • 2、 根据moduleName获取对应的模块缓存信息,如果有缓存直接序列化一下返回给js端
  • 3、 如果没有缓存则创建一份模块数据
  • 4、 创建的数据如果没有值,也返回空
  • 5、将新创建的模块信息添加到缓存里
  • 6、将数据序列化以后返回给js端

在上述过程中,可以看到有个createModule的操作。
继续看看createModule里发生了什么

folly::Optional JSINativeModules::createModule(
    Runtime &rt,
    const std::string &name) {

  ...

  // 如果m_genNativeModuleJS函数不存在,则使用__fbGenNativeModule做key去runtime的global对象上的value。
  // 这个value是js负责的赋值。
  // 可以理解为这个方法在native端调用。但是方法的实现是在js端。
  if (!m_genNativeModuleJS) {
    m_genNativeModuleJS =
        rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
  }

  // 同样的先查询缓存
  auto result = m_moduleRegistry->getConfig(name);
  // 如果命中缓存,但是没有值,直接返回空
  if (!result.hasValue()) {
    return folly::none;
  }
  // 如果缓存里没有,则调用js端实现的m_genNativeModuleJS函数,获取moduleinfo。
  // js端会创建一份js端该模块的注册信息,然后发送给native
  Value moduleInfo = m_genNativeModuleJS->call(
      rt,
      valueFromDynamic(rt, result->config),
      static_cast(result->index));
  
...
  // native端拿到js端返回的moduleinfo,包装一层作为native端的信息
  folly::Optional module(
      moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));

  ...
  // 返回包装后的模块信息
  return module;
}
 
 

先梳理一下过程

  • 1、先看一下m_genNativeModuleJS这个函数存不存在
  • 2、如果不存在需要去global对象去获取一下实现。这个函数的实现是在js端。
  • 3、同样先查一遍缓存
  • 4、如果查到了直接返回
  • 5、如果没有查到就调用m_genNativeModuleJS这个js端实现的函数创建对应的模块信息
  • 6、将模块信息添加到缓存
  • 7、 返回模块信息

此处有个m_genNativeModuleJS函数。

我们继续跟,看看m_genNativeModuleJS函数在js端的实现是怎么做的

// NativeModule.js
export type ModuleConfig = [
  string /* name */,
  ?Object /* constants */,
  ?$ReadOnlyArray /* functions */,
  ?$ReadOnlyArray /* promise method IDs */,
  ?$ReadOnlyArray /* sync method IDs */,
];

export type MethodType = 'async' | 'promise' | 'sync';

// 一、函数声明
function genModule(
  config: ?ModuleConfig, 
  moduleID: number,
): ?{
  name: string,
  module?: Object,
  ...
} {
  if (!config) {
    return null;
  }

  const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
  
  ...

  if (!constants && !methods) {
    // Module contents will be filled in lazily later
    return {name: moduleName};
  }

  const module = {};
  methods &&
    methods.forEach((methodName, methodID) => {
      const isPromise =
        promiseMethods && arrayContains(promiseMethods, methodID);
      const isSync = syncMethods && arrayContains(syncMethods, methodID);
      
      ...
      // 遍历每个方法,确定每个方法的类型。同步、异步、promise
      const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
      // 生成对应的js函数,然后存到module内
      module[methodName] = genMethod(moduleID, methodID, methodType);
    });
   // 将静态变量合并到module里
  Object.assign(module, constants);

  if (module.getConstants == null) {
    module.getConstants = () => constants || Object.freeze({});
  } else {
    console.warn(
      `Unable to define method 'getConstants()' on NativeModule '${moduleName}'. NativeModule '${moduleName}' already has a constant or method called 'getConstants'. Please remove it.`,
    );
  }

   ...
  // 最终在js端创建完js端的函数和静态变量以后,把信息返回给native端
  return {name: moduleName, module};
}

// export this method as a global so we can call it from native
//  二、赋值给global对象的__fbGenNativeModule属性。导出此方法,让native端可以拿到
global.__fbGenNativeModule = genModule;

这段代码概括来讲就干了俩事。

  • 1、声明一个genModule函数。在这个函数里创建对应的native模块信息,这个信息不会直接在js端使用。而是先返回给native端。在native端处理以后使用。
  • 2、把这个函数赋值给global对象上的__fbGenNativeModule属性。给native端调用。

梳理一下genModule是怎么做的

  • 1、获取模块配置信息
  • 2、如果没有方法也没有静态变量,直接返回
  • 3、遍历方法列表,逐个确认每个方法的类型:promise、同步、异步三种。
  • 4、不同的方法创建不同的函数然后添加到module内
  • 5、将静态变量合并到module内
  • 6、返回module给原生

看看genMethod这个函数。在js端是怎么给模块创建函数的

function genMethod(moduleID: number, methodID: number, type: MethodType) {
  let fn = null;
  // 如果是promise类型,最终返回一个函数,这个函数返回值是promise类型的对象
  if (type === 'promise') {
    fn = function promiseMethodWrapper(...args: Array) {
      // In case we reject, capture a useful stack trace here.
      const enqueueingFrameError: ExtendedError = new Error();
      return new Promise((resolve, reject) => {
        BatchedBridge.enqueueNativeCall(
          moduleID,
          methodID,
          args,
          data => resolve(data),
          errorData =>
            reject(updateErrorWithErrorData(errorData, enqueueingFrameError)),
        );
      });
    };
  } else {
    // 如果不是promise类型
    fn = function nonPromiseMethodWrapper(...args: Array) {
      const lastArg = args.length > 0 ? args[args.length - 1] : null;
      const secondLastArg = args.length > 1 ? args[args.length - 2] : null;
      const hasSuccessCallback = typeof lastArg === 'function';
      const hasErrorCallback = typeof secondLastArg === 'function';
      hasErrorCallback &&
        invariant(
          hasSuccessCallback,
          'Cannot have a non-function arg after a function arg.',
        );
      const onSuccess = hasSuccessCallback ? lastArg : null;
      const onFail = hasErrorCallback ? secondLastArg : null;
      const callbackCount = hasSuccessCallback + hasErrorCallback;
      args = args.slice(0, args.length - callbackCount);
      // 如果是同步函数。会调用原生端提供的callNativeSyncHook方法,
      // 通过原生端去生成对应的函数实现,然后把返回值拿来以后,最终存到module对象里。
      if (type === 'sync') {
        return BatchedBridge.callNativeSyncHook(
          moduleID,
          methodID,
          args,
          onFail,
          onSuccess,
        );
      } else {
        // 如果是异步函数,直接加入到js端的消息队列里。
        // 这是一个js向native端通讯的队列,队列里的内容都是异步执行的
        BatchedBridge.enqueueNativeCall(
          moduleID,
          methodID,
          args,
          onFail,
          onSuccess,
        );
      }
    };
  }
  fn.type = type;
  return fn;
}
  • 0、 创建一个fn对象
  • 1、如果是promise的返回一个函数,创建一个返回值是promise对象的函数赋值给fn
  • 2、 如果是同步函数,创建一个普通函数赋值给fn。这个普通函数的返回值是调用BatchedBridge.callNativeSyncHook的执行结果。
  • 3、 如果是异步函数。将没有返回值的普通函数赋值给fn。并在jsToNative的任务队列里,添加一个任务
  • 4、最终设置一下fn变量的函数的类型,返回把fn对象返回。

BatchedBridge.callNativeSyncHook最终是js端调用的一个函数global.nativeCallSyncHook,此函数js端负责调用,native端负责实现。js发起调用的时候可以直接找到对应的原生模块和方法,同步执行。

继续往下看两个地方:第一个是callNativeSyncHook的实现和enqueueNativeCall的实现

先看第一个callNativeSyncHook

// MessageQueue.js
callNativeSyncHook(
    moduleID: number,
    methodID: number,
    params: any[],
    onFail: ?Function,
    onSucc: ?Function,
  ): any {
    
    ...

    // 创建对应的回调,并记录一下
    this.processCallbacks(moduleID, methodID, params, onFail, onSucc);
    // 最终调用是native端的nativeCallSyncHook的实现
    return global.nativeCallSyncHook(moduleID, methodID, params);
  }

最终调用的是global.nativeCallSyncHook(moduleID, methodID, params)。 这个函数 js端负责调用。native端负责实现。
看一下native端对nativeCallSyncHook的实现

// JSIExecutor.cpp
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); }));

可以看到native端调用的是nativeCallSyncHook

// JSIExecutor.cpp
Value JSIExecutor::nativeCallSyncHook(const Value *args, size_t count) {
  if (count != 3) {
    throw std::invalid_argument("nativeCallSyncHook arg count must be 3");
  }

  if (!args[2].asObject(*runtime_).isArray(*runtime_)) {
    throw std::invalid_argument(
        folly::to("method parameters should be array"));
  }

  MethodCallResult result = delegate_->callSerializableNativeHook(
      *this,
      static_cast(args[0].getNumber()), // moduleId
      static_cast(args[1].getNumber()), // methodId
      dynamicFromValue(*runtime_, args[2])); // args

  if (!result.hasValue()) {
    return Value::undefined();
  }
  return valueFromDynamic(*runtime_, result.value());
}

// NativeToJsBridge.cpp
MethodCallResult callSerializableNativeHook(
      __unused JSExecutor &executor,
      unsigned int moduleId,
      unsigned int methodId,
      folly::dynamic &&args) override {
    return m_registry->callSerializableNativeHook(
        moduleId, methodId, std::move(args));
  }

可以看到原生端是直接通过moduleID,methodID和args调用了对应模块的对应函数,并把返回值返回给js端。
由此可以看到,rn框架里,js是可以同步调用native的。上述就是js同步执行native方法,然后把返回值返回给js端的实现

我们再看看异步调用的流程enqueueNativeCall

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();
    // 如果有nativeFlushQueueImmediate且本次调用距离上一次清空队列的时间大于5ms。
    // 则通过nativeFlushQueueImmediate主动把当前消息队列发给native端去执行。
    // 默认情况下是native端主动向js端取数据然后执行。只有大于5ms还没有取的时候,js会主动发起执行
    if (
      global.nativeFlushQueueImmediate &&
      now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS
    ) {
      const queue = this._queue;
      this._queue = [[], [], [], this._callID];
      this._lastFlush = now;
      // 主动将队列数据,发送给native端调用
      global.nativeFlushQueueImmediate(queue);
    }
    
    ...

  }

我们可以看到大部分入队操作都是紧紧把任务入队就结束了。等待native端来取出来队列然后执行。但是当native端超过5ms没有取过数据了。就会主动调用nativeFlushQueueImmediate方法,将任务队列push到native去执行。这个方法也是js端调用,native负责实现

native端对nativeFlushQueueImmediate的实现:

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) {
            if (count != 1) {
              throw std::invalid_argument(
                  "nativeFlushQueueImmediate arg count must be 1");
            }
            callNativeModules(args[0], false);
            return Value::undefined();
          }));

最终执行到native端的callNativeModules

整个初始化runtime的参数介绍完了。基本上js端对native端的调用流程也能整理出来了。

我们通过一个例子来总结一下js端调用native端的整个流程

// native端
//  CalendarManager.m
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
  RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}

// ---------------------------

// js端
import {NativeModules} from 'react-native';
const CalendarManager = NativeModules.CalendarManager;
export default CalendarManager;

CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');
  • 1、NativeModules 对应 NativeModulesProxy
  • 2、 NativeModules.CalendarManager 等价于调用 NativeModulesProxy中的get(“CalendarManager”)方法.
  • 3、 然后NativeModulesProxy先去内部缓存里查。
  • 4、如果查到了直接返回对应的模块信息。此时假定没有缓存
  • 5、如果没有查到就内部原生模块注册类的nativeModules->getModule(“CalendarManager”)方法
  • 6、进来以后也是查缓存。假定也没有缓存。
  • 7、 继续调用module = createModule(“CalendarManager”)函数
  • 8、然后判断m_genNativeModuleJS这个函数存不存在,如果不存在,则去global对象上取值
  • 9、然后再次查缓存,如果存在就返回。我们假定仍没有缓存
  • 10、 native调用js端实现的m_genNativeModuleJS函数。让js生成对应的模块信息
  • 11、来到js端执行genModule(CalendarManagerConfig,"CalendarManagerId")这个函数
  • 12、 从config中获取方法列表。
  • 13、 遍历方法列表
  • 14、 判断每个方法的类型 是promise 同步 异步。
  • 15、分别创建js端的函数。
  • 16、然后把创建的函数放到module对象里
  • 17、把module返回给native端
  • 18、 此时回到native端。将js端返回过来的module信息存到缓存里,然后返回给js端。
  • 19、 js端获取到CalendarManager模块信息。对应的模块在js端也有了对应的模块映射信息等内容。直接调用addEvent方法
  • 20、 这个方法是个异步方法。最终会添加加到JSToNative的消息队列里。
  • 20、假定如果上一次native端取队列的值超过了5ms。
  • 21、js端调用global.nativeFlushQueueImmediate(queue)主动调用native
  • 22、 native端执行callNativeModules("CalendarManager", "addEvent").
  • 23、 然后native端执行CalendarManager模块的addEvent方法。
  • 24、 最终控制台输出内容。

整个调用流程结束

7、 加载jsbundle代码

此部分逻辑和上一部分初始化bridge 是同时异步执行的。
此部分相对比较简单,就是加载一下指定路径的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) {
#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include()
        id loadingView = [weakSelf moduleForName:@"DevLoadingView"
                                                      lazilyLoadIfNecessary:YES];
        // 加载过程中,更新加载进度条
        [loadingView updateProgress:progressData];
#endif
      }];

...


- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
{
  // 发送通知 RCTBridgeWillDownloadScriptNotification 将要下载script
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  [center postNotificationName:RCTBridgeWillDownloadScriptNotification object:_parentBridge];
  [_performanceLogger markStartForTag:RCTPLScriptDownload];
  NSUInteger cookie = RCTProfileBeginAsyncEvent(0, @"JavaScript download", nil);

  // Suppress a warning if RCTProfileBeginAsyncEvent gets compiled out
  (void)cookie;

  RCTPerformanceLogger *performanceLogger = _performanceLogger;
  // 创建load成功的回调
  RCTSourceLoadBlock onSourceLoad = ^(NSError *error, RCTSource *source) {
    
    ...

    NSDictionary *userInfo = @{
      RCTBridgeDidDownloadScriptNotificationSourceKey : source ?: [NSNull null],
      RCTBridgeDidDownloadScriptNotificationBridgeDescriptionKey : self->_bridgeDescription ?: [NSNull null],
    };
    
    // 发送通知RCTBridgeDidDownloadScriptNotification
    [center postNotificationName:RCTBridgeDidDownloadScriptNotification object:self->_parentBridge userInfo:userInfo];

    _onSourceLoad(error, source);
  };
  
  // 开始load
  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 if (!self.bundleURL) {
    NSError *error = RCTErrorWithMessage(
        @"No bundle URL present.\n\nMake sure you're running a packager "
         "server or have included a .jsbundle file in your application bundle.");
    onSourceLoad(error, nil);
  } else {
    //  开发环境下,新项目执行此处
    __weak RCTCxxBridge *weakSelf = self;
    [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL
                              onProgress:onProgress
                              onComplete:^(NSError *error, RCTSource *source) {
                                if (error) {
                                  [weakSelf handleError:error];
                                  return;
                                }
                                onSourceLoad(error, source);
                              }];
  }
}
// RCTJavaScriptLoader.mm
+ (void)loadBundleAtURL:(NSURL *)scriptURL
             onProgress:(RCTSourceLoadProgressBlock)onProgress
             onComplete:(RCTSourceLoadBlock)onComplete
{
  int64_t sourceLength;
  NSError *error;
  // 尝试同步加载
  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;
  // 再尝试异步加载,成功返回,失败的话调用回调返回失败
  if (isCannotLoadSyncError) {
    attemptAsynchronousLoadOfBundleAtURL(scriptURL, onProgress, onComplete);
  } else {
    onComplete(error, nil);
  }
}

我们看一下加载成功后要做啥

8、 执行js代码

在6、7都执行完毕后,才会开始执行本步骤。

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执行成功的回调
  dispatch_block_t completion = ^{
    // Log start up metrics early before processing any other js calls
    [self logStartupFinish];
    // 处理pending中的native对js端的调用任务
    [self _flushPendingCalls];

    // 到主线程上发送RCTJavaScriptDidLoadNotification的通知
    dispatch_async(dispatch_get_main_queue(), ^{
      [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
                                                          object:self->_parentBridge
                                                        userInfo:@{@"bridge" : self}];

      // Starting the display link is not critical to startup, so do it last
      [self ensureOnJavaScriptThread:^{
        // 将js的调用和渲染帧关联到一起。
        [self->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]];
      }];
    });
  };

  // 根据参数,同步或者异步 开始执行script
  if (sync) {
    [self executeApplicationScriptSync:sourceCode url:self.bundleURL];
    completion();
  } else {
    [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
  }

  ...
}

整体概括一下就是两步

  • 1、创建一个执行js代码执行成功后的回调
  • 2、根据参数去确定同步执行还是异步执行js代码

不管同步还是异步,最终执行的方法都是同一个方法

- (void)executeApplicationScript:(NSData *)script url:(NSURL *)url async:(BOOL)async
{
  [self _tryAndHandleError:^{
    NSString *sourceUrlStr = deriveSourceURL(url);
    // 发送RCTJavaScriptWillStartExecutingNotification的通知
    [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartExecutingNotification
                                                        object:self->_parentBridge
                                                      userInfo:@{@"bridge" : self}];

    auto reactInstance = self->_reactInstance;
    // 如果是RAM中的script就走这里
    if (isRAMBundle(script)) {
      ...
      auto ramBundle = std::make_unique(sourceUrlStr.UTF8String);
      std::unique_ptr scriptStr = ramBundle->getStartupCode();
      ...
      if (reactInstance) {
        auto registry =
            RAMBundleRegistry::multipleBundlesRegistry(std::move(ramBundle), JSIndexedRAMBundle::buildFactory());
        reactInstance->loadRAMBundle(std::move(registry), std::move(scriptStr), sourceUrlStr.UTF8String, !async);
      }
    } else if (reactInstance) {
      //  冷启动执行该方法
      reactInstance->loadScriptFromString(std::make_unique(script), sourceUrlStr.UTF8String, !async);
    } else {
      std::string methodName = async ? "loadBundle" : "loadBundleSync";
      throw std::logic_error("Attempt to call " + methodName + ": on uninitialized bridge");
    }
  }];
}

因为整个流程是本地初始化一个空项目然后再开发环境下运行的。所以本文案例代码会从reactInstance->loadScriptFromString执行

// Instance.cpp
void Instance::loadScriptFromString(
    std::unique_ptr string,
    std::string sourceURL,
    bool loadSynchronously) {
  ...
  if (loadSynchronously) {
    loadBundleSync(nullptr, std::move(string), std::move(sourceURL));
  } else {
    loadBundle(nullptr, std::move(string), std::move(sourceURL));
  }
}

同样的最终都是同一个方法loadBundle

void Instance::loadBundle(
    std::unique_ptr bundleRegistry,
    std::unique_ptr string,
    std::string sourceURL) {
   // 对js的调用+1
  callback_->incrementPendingJSCalls();
  ...
  nativeToJsBridge_->loadBundle(
      std::move(bundleRegistry), std::move(string), std::move(sourceURL));
}
// NativeToJsBridge.cpp
void NativeToJsBridge::loadBundle(
    std::unique_ptr bundleRegistry,
    std::unique_ptr 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();
        if (bundleRegistry) {
          executor->setBundleRegistry(std::move(bundleRegistry));
        }
        try {
          executor->loadBundle(
              std::move(*startupScript), std::move(startupScriptSourceURL));
        } catch (...) {
          m_applicationScriptHasFailure = true;
          throw;
        }
      });
}

执行到NativeToJsBridge里。可以看到,此处本质上就是创建个任务然后入队。
看一下任务内容executor->loadBundle(std::move(*startupScript), std::move(startupScriptSourceURL));

void JSIExecutor::loadBundle(
    std::unique_ptr script,
    std::string sourceURL) {
  SystraceSection s("JSIExecutor::loadBundle");

  ...

  runtime_->evaluateJavaScript(
      std::make_unique(std::move(script)), sourceURL);
  flush();
  
  ...

}

最终的终点是到了jsruntime这里。
整个内容也是两步

  • 1、 runtime_->evaluateJavaScript
    runtime_:可以认为是js引擎。runtime_->evaluateJavaScript 此处就到了引擎执行代码阶段。
  • 2、 执行flush()函数
// JSIExecutor.cpp
void JSIExecutor::flush() {

  ...
  // flushedQueue_ 记录着js端对native端的调用任务。
  if (flushedQueue_) {
    // 执行完js后,如果此时js端发生了对native的调用。此处立即执行一下。
    callNativeModules(flushedQueue_->call(*runtime_), true);
    return;
  }

  // 从runtime上的global对象是获取batchedBridge。如果js端发生了对native的调用,此时这个bridge是肯定会有值的。
  Value batchedBridge =
      runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
  // 如果此时是undefined,说明js端没有主动调用native模块,所以没有赋值
  if (!batchedBridge.isUndefined()) {
    // 此时native端,主动发起一起bindBridge,将native和js连接到一起
    bindBridge();
    // 然后再调用native模块
    callNativeModules(flushedQueue_->call(*runtime_), true);
  } else if (delegate_) {
    callNativeModules(nullptr, true);
  }
}

flushedQueue_整个队列记录着js对native端的调用任务。在js初始化执行的过程中。有可能产生了js对native的调用。初始化js后,native端要先把这些调用执行完。避免遗漏一些初始化操作。
batchedBridge:如果js发生了对native的调用,此时这个对象是存在的。如果没有发生对native的调用。native端主动发起一起BindBridge。这样确保js和native的通道是畅通的。便于后面的交互。

void JSIExecutor::bindBridge() {
  std::call_once(bindFlag_, [this] {
    SystraceSection s("JSIExecutor::bindBridge (once)");
    Value batchedBridgeValue =
        runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined()) {
      throw JSINativeException(
          "Could not get BatchedBridge, make sure your bundle is packaged correctly");
    }

    Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
    callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "callFunctionReturnFlushedQueue");
    invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "invokeCallbackAndReturnFlushedQueue");
    flushedQueue_ =
        batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
  });
}

还记得之前初始化runtime的时候,js端和native都往global对象上添加了一些属性。基本上都是用来处理jsToNative调用逻辑的
bindBridge函数内也是往这个global对象上添加了属性。此次添加的基本上都是nativeToJs的调用相关的内容

callFunctionReturnFlushedQueue: 调用js端的方法,然后返回flushqueue(里面记录jsToNative的调用)
invokeCallbackAndReturnFlushedQueue: 执行js端记录的回调,然后返回flushqueue
flushedQueue: flushqueue对象

看一下每个函数的实现

callFunctionReturnFlushedQueue(
    module: string, // 模块名称id
    method: string, // 方法名称id
    args: any[], // 参数
  ): null | [Array, Array, Array, number] {
    // 执行函数
    this.__guard(() => {
      this.__callFunction(module, method, args);
    });
    
    // 返回队列
    return this.flushedQueue();
  }

__callFunction(module: string, method: string, args: any[]): void {
    this._lastFlush = Date.now();
    this._eventLoopStartTime = this._lastFlush;
    ...
    // 获取模块方法
    const moduleMethods = this.getCallableModule(module);
    ...
    // 根据方法id取出来对应的方法执行
    moduleMethods[method].apply(moduleMethods, args);
    ...
  }

flushedQueue(): null | [Array, Array, Array, number] {
    this.__guard(() => {
      this.__callImmediates();
    });
    // 将当期队列信息返回给native端,并清空js端队列
    const queue = this._queue;
    this._queue = [[], [], [], this._callID];
    return queue[0].length ? queue : null;
  }

callFunctionReturnFlushedQueue: 做的事情是native端发起对js端的调用,在js端查找对应的模块和方法,执行之后,在将当前的flushqueue队列信息返回给native端

invokeCallbackAndReturnFlushedQueue(
    cbID: number,
    args: any[],
  ): null | [Array, Array, Array, number] {
    // 根据callbackid执行对应的回调
    this.__guard(() => {
      this.__invokeCallback(cbID, args);
    });
    // 返回flushqueue队列的数据
    return this.flushedQueue();
  }

__invokeCallback(cbID: number, args: any[]) {
    this._lastFlush = Date.now();
    this._eventLoopStartTime = this._lastFlush;
  
    // 查询callbackid,取出来callback函数
    const callID = cbID >>> 1;
    const isSuccess = cbID & 1;
    const callback = isSuccess
      ? this._successCallbacks.get(callID)
      : this._failureCallbacks.get(callID);

    ...
    // 如果没有callback直接返回
    if (!callback) {
      return;
    }
    // 删除对应的callback
    this._successCallbacks.delete(callID);
    this._failureCallbacks.delete(callID);
    // 执行callback
    callback(...args);

    ...
  }

invokeCallbackAndReturnFlushedQueue:这个方法就是调用回调然后返回flushqueue队列的数据。
flushedQueue: 未找到实际处理的代码,大胆猜测是对应js端的flushedQueue函数。获取flushqueue队列数据。

关于JSCRuntime中的global对象我们再整理一下:

global = {
        nativeModuleProxy: NativeModuleProxy,
        nativeFlushQueueImmediate: (flushQueue) => { callNativeModules(flushQueue) },
        nativeCallSyncHook: (args) => {return nativeCallSyncHook(args, count)},
        __fbBatchedBridge: BatchedBridge, // js端添加的
        __fbGenNativeModule:(moduleCondig, moduleId)=> {moduleName, module} ,
        callFunctionReturnFlushedQueue: (module,method, args) => flushQueue, 
        invokeCallbackAndReturnFlushedQueue:(cbId, args) => flushQueue,
        flushedQueue: () => flushQueue,
    }

nativeModuleProxy: 记录原生模块注册信息,
nativeFlushQueueImmediate: js负责调用,native负责实现。
nativeCallSyncHook: js负责调用,native负责实现。
__fbBatchedBridge: jsToNative的调用的消息队列const BatchedBridge: MessageQueue = new MessageQueue();
__fbGenNativeModule: native端调用,js负责实现。
callFunctionReturnFlushedQueue: native端调用,js端负责实现。
invokeCallbackAndReturnFlushedQueue: native端调用,js端负责实现。
flushedQueue: native端调用。js负责实现。

梳理一下整体逻辑
在构造玩bridge和加载完jsbundle代码以后。开始执行js代码。
先创建个执行成功后的回调,
然后开始执行js。
对js的执行本质,是往js消息线程的任务队列里添加一个任务
任务的内容是让jsctime执行js代码,然后执行完以后还要立即处理一下在这个期间产生的js对native的调用。
然后一开始创建的执行成功的回调作为下一个任务执行。

自此,初始化的js代码执行完毕。

二. 创建rootView


    1. 创建视图
- (instancetype)initWithBridge:(RCTBridge *)bridge
                    moduleName:(NSString *)moduleName
             initialProperties:(NSDictionary *)initialProperties
{

  ...

  if (self = [super initWithFrame:CGRectZero]) {
    self.backgroundColor = [UIColor whiteColor];

    _bridge = bridge;
    _moduleName = moduleName;
    _appProperties = [initialProperties copy];
    _loadingViewFadeDelay = 0.25;
    _loadingViewFadeDuration = 0.25;
    _sizeFlexibility = RCTRootViewSizeFlexibilityNone;
    _minimumSize = CGSizeZero;

    // 添加监听
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(bridgeDidReload)
                                                 name:RCTJavaScriptWillStartLoadingNotification
                                               object:_bridge];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(javaScriptDidLoad:)
                                                 name:RCTJavaScriptDidLoadNotification
                                               object:_bridge];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(hideLoadingView)
                                                 name:RCTContentDidAppearNotification
                                               object:self];

    ...
    
    // 显示loading
    [self showLoadingView];

    // Immediately schedule the application to be started.
    // (Sometimes actual `_bridge` is already batched bridge here.)
    // 4. 处理加载完毕后的逻辑
    [self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];
  }

  ...

  return self;
}
    1. 添加监听
[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(bridgeDidReload)
                                                 name:RCTJavaScriptWillStartLoadingNotification
                                               object:_bridge];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(javaScriptDidLoad:)
                                                 name:RCTJavaScriptDidLoadNotification
                                               object:_bridge];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(hideLoadingView)
                                                 name:RCTContentDidAppearNotification
                                               object:self];
    1. 显示加载画面
[self showLoadingView];
    1. 处理js加载完毕后的逻辑
- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
  ...

  if (!bridge.valid) {
    return;
  }

  [_contentView removeFromSuperview];
  _contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
                                                    bridge:bridge
                                                  reactTag:self.reactTag
                                            sizeFlexiblity:_sizeFlexibility];

  [self runApplication:bridge];

  _contentView.passThroughTouches = _passThroughTouches;
  [self insertSubview:_contentView atIndex:0];

  if (_sizeFlexibility == RCTRootViewSizeFlexibilityNone) {
    self.intrinsicContentSize = self.bounds.size;
  }
}

- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @"";
  // 项目启动时候初始参数
  NSDictionary *appParameters = @{
    @"rootTag" : _contentView.reactTag,
    @"initialProps" : _appProperties ?: @{},
  };

  RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
  [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[ moduleName, appParameters ] completion:NULL];
}

可以看到当前的runApplication方法,是做了一个入队的操作。 发起对js端AppRegistry模块runApplication方法的调用。可以理解为这个方法是js端的启动入口方法。

// RCTBridge.mm
- (void)enqueueJSCall:(NSString *)module
               method:(NSString *)method
                 args:(NSArray *)args
           completion:(dispatch_block_t)completion
{
  if (!self.valid) {
    return;
  }

  ...

  __weak __typeof(self) weakSelf = self;
  // 确保在加载完成以后执行
  [self _runAfterLoad:^() {
    
    ...

    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (!strongSelf) {
      return;
    }

    // 主要方法,内部主要逻辑是把对js的调用,添加到_jsMessageThread的执行队列里
    if (strongSelf->_reactInstance) {
      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
      // 执行完毕的回调调价到_jsMessageThread的执行队列里,会在上一步所有任务执行完后,最后执行回调
      if (completion) {
        if (strongSelf->_jsMessageThread) {
          strongSelf->_jsMessageThread->runOnQueue(completion);
        } else {
          RCTLogWarn(@"Can't invoke completion without messageThread");
        }
      }
    }
  }];

  ...
}
// Instance.cpp
void Instance::callJSFunction(
    std::string &&module,
    std::string &&method,
    folly::dynamic &¶ms) {
  callback_->incrementPendingJSCalls();
  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) {
    
    ...

    executor->callFunction(module, method, arguments);
  });
}
// JSIExecutor.cpp
void JSIExecutor::callFunction(
    const std::string &moduleId,
    const std::string &methodId,
    const folly::dynamic &arguments) {
  
  ...
  
  // 如果callFunctionReturnFlushedQueue_不存在,表示native和js端直接通讯的bridge还未打通,此时需要手动bindBridge一下,打通直接通信的通道
  if (!callFunctionReturnFlushedQueue_) {
    bindBridge();
  }

  ...

  Value ret = Value::undefined();
  try {
    scopedTimeoutInvoker_(
        [&] {
          // 执行callFunctionReturnFlushedQueue_,返回一个消息列队。里面记录着js端对native的调用
          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模块,即处理js对native端的调用
  callNativeModules(ret, true);
}

callFunctionReturnFlushedQueue_: 这个函数之前已经分析过,是native端调用,js端负责实现。最终把js端的消息队列返回给native端。因为在native调用js的过程中,有可能产生了js对native的调用。native需要处理一下。

自此 整个rn应用的启动就完毕了。
所有细节从代码层面已经梳理了一遍了。另外由于个人水平有限,无法确保梳理的一定是正确的,大家阅读文章的时候,请时刻怀疑质疑的想法去思考对照,文中观点仅供参考。希望大家多多指点文中有误的地方,及时纠正,避免误导他人。

你可能感兴趣的:(react-native框架中iOS端启动流程(超级详细))