耗时4天写的日程小程序

导语:其实原本是想编写一篇 react-native (下文简称 rn) 在 iOS 中如何实现 jsbridge 的文章;相信看过官方文档的同学都清楚 rn 和 iOS 通信使用了一个叫RCTBridgeModule的模块去实现;相信大家与我一样,不能知其然不知其所以然;所以决定去翻一番 rn 的源码,一探其 rn 与 iOS 通信的机制。

本文将会从 iOS 中使用 rn 初始化页面逐步分析源码,其内容可能存在大量的源码,但是笔者只会列出较为重要的部分;如有兴趣可自行翻阅并阅读;其次本文和传统的文章不同之处,是将会先告诉读者对应的部分的流程,然后根据流程进行分析,这样就避免了大家在一味的阅读源码,到最后没有一个整体的流程。

声明: 本文所使用的 rn 版本为0.63.0

# 缘起

在使用react-native init RNJsBridgeiOS项目之后在ios/AppDelegate.m中有如下代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#ifdef FB_SONARKIT_ENABLED
  InitializeFlipper(application);
#endif

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"RNJsBridgeiOS"
                                            initialProperties:nil];

//...
  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

我们发现在这部分代码中一共做了三件事情:

  • 初始化一个 RCTBridge 实现 加载 jsbundle 的方法;
  • 利用 RCTBridge 初始化一个 RCTRootView
  • RCTRootView 赋值给 controller 的 view 即可;

顾名思义 RCTRootView 给人的第一直觉是做 View 初始化的,我们先来看看 RCTRootView 到底做了啥?和我们本次想要讲解的 rn 原生通信原理解析有何关系?

# 先阅 RCTRootView

看到现在可能有同学在想,为啥还没直入主题,讲解 RN 如何和 iOS 通信的;之所以先简单讲述一下 RCTRootView 是为了让各位同学更好的理解 RCTRootViewRCTBridge 的关系;因为很多同学在网上查阅 RN 页面初始化发现有很多种方式,其实你会发现都是一样的。为何?好,请看下面:

RCTRootView.h 声明文件中我们发现如下两个讯息:

  • RCTRootView 继承自 UIView,表面其是一个 UI 展示的组件;
  • RCTRootView 加载资源初始化有两种方式initWithBridgeinitWithBundleURL两种方式
// RCTRootView.h
@interface RCTRootView : UIView

- (instancetype)initWithBridge:(RCTBridge *)bridge
                    moduleName:(NSString *)moduleName
             initialProperties:(nullable NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(nullable NSDictionary *)initialProperties
                    launchOptions:(nullable NSDictionary *)launchOptions;
// ...
@end

再来看看RCTRootView.minitWithBridgeinitWithBundleURL两种方法的实现:

//RCTRootView.m
@implementation RCTRootView {
  RCTBridge *_bridge;
  NSString *_moduleName;
  RCTRootContentView *_contentView;
  BOOL _passThroughTouches;
  CGSize _intrinsicContentSize;
}

- (instancetype)initWithBridge:(RCTBridge *)bridge
                    moduleName:(NSString *)moduleName
             initialProperties:(NSDictionary *)initialProperties
{
  // ...

  if (self = [super initWithFrame:CGRectZero]) {
    // ...
    // 注册名为RCTJavaScriptWillStartLoadingNotification的消息通知,当jsbunlde将要加载时会触发bridgeDidReload
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(bridgeDidReload)
                                                 name:RCTJavaScriptWillStartLoadingNotification
                                               object:_bridge];
    // 注册名为RCTJavaScriptDidLoadNotification的消息通知,当jsbundle执行完成之后会会调用javaScriptDidLoad
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(javaScriptDidLoad:)
                                                 name:RCTJavaScriptDidLoadNotification
                                               object:_bridge];
    // 注册名为RCTContentDidAppearNotification的消息通知,当内容以及展示则会关闭加载进度条
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(hideLoadingView)
                                                 name:RCTContentDidAppearNotification
                                               object:self];
    // ios版本的rn还支持展示loadingview哦
    [self showLoadingView];

    // Immediately schedule the application to be started.
    // (Sometimes actual `_bridge` is already batched bridge here.)
    [self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];
  }
  return self;
}
// initWithBundleURL的实现
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(NSDictionary *)initialProperties
                    launchOptions:(NSDictionary *)launchOptions
{
  // 也是和initWithBridge一样,只是不需要自己手动实例化 RCTBridge
  RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL moduleProvider:nil launchOptions:launchOptions];

  return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}

- (void)javaScriptDidLoad:(NSNotification *)notification
{
  // 获取到RCTBridge的实例batchedBridge(可能有点超前了,后面会将)
  RCTBridge *bridge = notification.userInfo[@"bridge"];
  if (bridge != _contentView.bridge) {
    [self bundleFinishedLoading:bridge];
  }
}

- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
  // ...
  [_contentView removeFromSuperview];
  _contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
                                                    bridge:bridge
                                                  reactTag:self.reactTag
                                           sizeFlexiblity:_sizeFlexibility];
  // 利用RCTBridge调用js方法,启动页面
  [self runApplication:bridge];
  // 展示页面
  [self insertSubview:_contentView atIndex:0];
}

- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @"";
  NSDictionary *appParameters = @{
    @"rootTag" : _contentView.reactTag,
    @"initialProps" : _appProperties ?: @{},
  };
  // 执行JavaScript中的方法AppRegistry.runApplication
  [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[ moduleName, appParameters ] completion:NULL];
}
@end

查阅上面的源码我们发现:

  1. initWithBundleURLinitWithBridge两种实现方式,其本质都是使用的initWithBridge将 RCTBridge 传入;
  2. 在 RCTBridge 中 RCTRootView 注册了各种监听,当资源在加载完成之后执行 react 代码并展示页面

好吧,我承认,如果您仔细看到这里,是不是有很多疑问;什么 RCTRootView, RCTBridge , batchedBridge , enqueueJSCall,what , 这些都是个啥?下面我将一一告诉你。

# 名词及 RN 简易流程

如果你看了上面【先阅 RCTRootView】 部分,相信已经迫不及待想要知道上面的那些名词都是啥,以及 RN 加载的简易流程(tips: 为什么先是简易流程?因为作者不想一下子灌输太多的概念让大家蒙圈,详细流程请见文末总结处 RN 加载时序图)。

下面我们开始逐步了解上面每一个部分吧;

# RCTBridge 初始化

与其说是 RCTBridge 的初始化倒不如说是 RN 整个加载的初始化,为什么这样说呢?下面我们挨个挨个查阅;首先按照我们最开始初始化 RCTBridge 中比较重要的几个方法:

// RCTBridge.m
// ...
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
{
  // RCTBridge initWithDelegate的初始化方法都会进入到initWithDelegate_bundleURL_moduleProvider_launchOptions方法中
  return [self initWithDelegate:delegate bundleURL:nil moduleProvider:nil launchOptions:launchOptions];
}

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                   moduleProvider:(RCTBridgeModuleListProvider)block
                    launchOptions:(NSDictionary *)launchOptions
{
  // RCTBridge initWithDelegate的初始化方法都会进入到initWithDelegate_bundleURL_moduleProvider_launchOptions方法中
  return [self initWithDelegate:nil bundleURL:bundleURL moduleProvider:block launchOptions:launchOptions];
}

- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
                       bundleURL:(NSURL *)bundleURL
                  moduleProvider:(RCTBridgeModuleListProvider)block
                   launchOptions:(NSDictionary *)launchOptions
{
  if (self = [super init]) {
    _delegate = delegate;
    _bundleURL = bundleURL;
    _moduleProvider = block;
    _launchOptions = [launchOptions copy];
    // 看这里
    [self setUp];
  }
  return self;
}

- (void)setUp
{
  // 获取bridgeClass 默认是RCTCxxBridge
  Class bridgeClass = self.bridgeClass;
  // 只有bundleURL的值发生变化才会更新bundleURL
  NSURL *previousDelegateURL = _delegateBundleURL;
  _delegateBundleURL = [self.delegate sourceURLForBridge:self];
  if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
    _bundleURL = _delegateBundleURL;
  }
  // 初始化RTCxxBridge
  self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
  // 启动RTCxxBridge
  [self.batchedBridge start];
}
// 使用RCTCxxBridge也就是self.batchedBridge
- (Class)bridgeClass
{
  return [RCTCxxBridge class];
}
// ...

RCTBridge 初始化方法一共做了如下几件事情:

  1. 调用 init 初始化RCTBridge实例;
  2. 调用RTCxxBridgeinitWithParentBridge方法初始化 RTCxxBridge 实例
  3. 调用self.batchedBridge的 start 方法启动

# RCTxxBridge start

上面部分RCTBridge初始化了 RCTxxBridge 的实例,并调用了start方法; RCTxxBridge 的 star 方法执行步骤如图所示:

;

下面我们就来具体看看:

// RCTxxBridge.mm

- (void)start
{
  //1. 发送RCTJavaScriptWillStartLoadingNotification消息通知以供RCTRootView接收并处理
  [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
                                                      object:_parentBridge
                                                    userInfo:@{@"bridge" : self}];

  //2. 提前设置并开启JS线程 _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
  // 启动JS线程
  [_jsThread start];

  dispatch_group_t prepareBridge = dispatch_group_create();
  //3. 注册native modules,主要是在初始化RCTBridge使用initWithBundleURL_moduleProvider_launchOptions中的moduleProvider block返回值的native modules;
  [self registerExtraModules];
  // 重点:注册所遇的自定义Native Module;包括你在rn官网上看到的原生模块定义以及RN自带的Text,Date等原生组件
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
    // 初始化所有懒加载的native module

  [self registerExtraLazyModules];

  // 其实这里不会做任何事情,详情请见initializeBridge
  _reactInstance.reset(new Instance);

  __weak RCTCxxBridge *weakSelf = self;

  //4. 准备executor factory; 看RCTBridge是否指定了executorClass
  std::shared_ptr<JSExecutorFactory> executorFactory;
  if (!self.executorClass) {// 如果没有指定executorClass 但是实现了RCTCxxBridgeDelegate协议,那么就使用jsExecutorFactoryForBridge的方式 准备 executor factory 否则就使用make_shared初始化一个空的JSCExecutorFactory
    if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
      id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>)self.delegate;
      executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
    }
    if (!executorFactory) {
      executorFactory = std::make_shared<JSCExecutorFactory>(nullptr);
    }
  } else {// 如果指定了 executorClass  就使用指定的executorClass 初始化;一般RCTObjcExecutorFactory为开发环境使用的
    id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
    executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
      if (error) {
        [weakSelf handleError:error];
      }
    }));
  }

  // 5. module初始化完成就初始化底层Instance实例,也就是_reactInstance
  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    // 利用executorFactory来initializeBridge 方法;完成初始化_reactInstance(也就是Instance)
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];

  //6. 异步加载js代码
  dispatch_group_enter(prepareBridge);
  __block NSData *sourceCode;
  [self
      loadSource:^(NSError *error, RCTSource *source) {
        if (error) {
          [weakSelf handleError:error];
        }

        sourceCode = source.data;
        dispatch_group_leave(prepareBridge);
      }
      onProgress:^(RCTLoadingProgress *progressData) {
#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include(<React/RCTDevLoadingViewProtocol.h>)
        id<RCTDevLoadingViewProtocol> loadingView = [weakSelf moduleForName:@"DevLoadingView"
                                                      lazilyLoadIfNecessary:YES];
        [loadingView updateProgress:progressData];
#endif
      }];

  // 7. 等待native moudle 和 JS 代码加载完毕后就执行JS; dispatch_group_t和dispatch_group_notify联合使用保证异步代码同步按顺序执行
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
    RCTCxxBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      // 重点,执行JS代码;后面我们会具体展开分析
      [strongSelf executeSourceCode:sourceCode sync:NO];
    }
  });
}

- (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
  __weak RCTCxxBridge *weakSelf = self;
  // 创建消息队列
  _jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) {
    if (error) {
      [weakSelf handleError:error];
    }
  });

  // This can only be false if the bridge was invalidated before startup completed
  if (_reactInstance) {
    [self _initializeBridgeLocked:executorFactory];
  }

}

- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
  std::lock_guard<std::mutex> guard(_moduleRegistryLock);
  // This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
  _reactInstance->initializeBridge(
      std::make_unique<RCTInstanceCallback>(self),
      executorFactory,
      _jsMessageThread,
      [self _buildModuleRegistryUnlocked]);
  _moduleRegistryCreated = YES;
}

讲真,RCTxxBridgestart 方法中,代码比较多;现在我们来梳理一下如上的 7 个步骤:

  1. 发送 RCTJavaScriptWillStartLoadingNotification 消息通知以供 RCTRootView 接收并处理;

  2. 开启 js 线程 _jsThread, 并绑定了 runRunLoop; 后续所有的 js 代码都在这个线程里面执行

  3. 注册所有 native modules

  • 注册所有的自定义 JS 和 iOS 通信的 native module;包括 RN 自带的 Text, Date 等;

  • 初始化阶段编写的 module: 主要是在初始化 RCTBridge 使用 initWithBundleURL_moduleProvider_launchOptions 中的 moduleProvider block 返回值的 native modules; native module 会被转换成RCTModuleData分别存入字典和数组中;

  • 所有懒加载的 native modules; 主要是在 chrome 浏览器调试时,动态的注入 native modules;

  1. 准备 jsNative 之间的桥和 js 运行环境,初始化 JSExecutorFactory 实例(顾名思义,JSExecutorFactory 是一个 JSExecutor 的工厂,也就是负责生产 JSExecutor 实例的工厂;

  2. 然后在 JS 线程中创建 JS 的 RCTMessageThread,初始化_reactInstance(Instance 实例);

  3. 异步加载 js 代码

  4. 等待 native modulesjs 代码都加载完成则执行 js 代码;

我们发现,react-native 中很多初始化的工作都是异步执行的(使用dispatch_group_t);但是最后都会使用dispatch_group_notify,等待所有的异步初始化工作执行完成之后,才会执行最后的 js 代码执行操作。

# JSExecutorFactory 创建了什么

上面谈到了通过创建一个 JSExecutorFactory 来创建 Instance;下面我们就来看看 JSExecutorFactory 到底是干嘛的。

JSExecutorFactory,顾名思义用于生产 JSExecutor 实例,JSExecutor 用于执行 JS,也是 JS 和 Native 之间的桥梁。无论是 Native call JS 还是 JS call Native,JSExecutor 都起到了至关重要的作用。

RCTxxBridge 的三种情况创建 JSExecutorFactory ;我们知道默认的情况走的是第二种,也就是默认没有设置 RCTBridge 的executorClass;

默认的情况 JSExecutorFactoryCxxBridge 目录下:

// JSCExecutorFactory.mm

// 有一个createJSExecutor方法
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
    std::shared_ptr<ExecutorDelegate> delegate,
    std::shared_ptr<MessageQueueThread> __unused jsQueue)
{
  // ...
  // 默认的情况创建的是JSIExecutor;但是具体的createJSExecutor方法在什么时候调用,还得看下面的分析;只是在这里,我们清楚了 createJSExecutor 创建的是 JSIExecutor
  return std::make_unique<JSIExecutor>(
      facebook::jsc::makeJSCRuntime(), delegate, JSIExecutor::defaultTimeoutInvoker, std::move(installBindings));
}

# RCTMessageThread 是做什么的

在 Instance 初始化之前创建了一个消息队列;那么消息队列是干嘛的呢?简单来说就是提供对象的队列执行,内部使用 CFRunLoop 来实现;此处不做具体分析。后面会介绍 RCTMessageThread 的具体作用。

# Instance 初始化

通过上面的 RCTxxBridgestart 方法调用链,我们发现在_initializeBridgeLocked中初始化了_reactInstance;

// RCTxxBridge.mm
- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
  std::lock_guard<std::mutex> guard(_moduleRegistryLock);
  // 初始化_reactInstance
  _reactInstance->initializeBridge(
      std::make_unique<RCTInstanceCallback>(self),
      executorFactory,
      _jsMessageThread,
      [self _buildModuleRegistryUnlocked]);
  _moduleRegistryCreated = YES;
}


// Instance.cpp
void Instance::initializeBridge(
    std::unique_ptr<InstanceCallback> callback,
    std::shared_ptr<JSExecutorFactory> jsef,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<ModuleRegistry> moduleRegistry) {
   jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
    // 使用JSExecutorFactory工厂创建一个JSExecutor来初始化NativeToJsBridge
    nativeToJsBridge_ = std::make_shared<NativeToJsBridge>(
        jsef.get(), moduleRegistry_, jsQueue, callback_);
    // 执行initializeRuntime方法
    nativeToJsBridge_->initializeRuntime();

     // 当NativeToJsBridge被创建之后,jsi::Runtime就会存在,当然 前创建 js消息队列也会存在,此时可以将当前创建的NativeToJsBridge放入消息队列中(此处我们暂不具体深究消息队列是干嘛的,后面会具体讲述)
    jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);

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

}

# NativeToJsBridge 的初始化

NativeToJsBridge 的初始化是在 InstanceinitializeBridge 方法中触发的,下面我们就来具体 NativeToJsBridge 是干嘛的;

// NativeToJsBridge.cpp
// NativeToJsBridge的初始化
NativeToJsBridge::NativeToJsBridge(
    JSExecutorFactory *jsExecutorFactory,
    std::shared_ptr<ModuleRegistry> registry,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<InstanceCallback> callback)
    : m_destroyed(std::make_shared<bool>(false)),
      m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
      m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
      m_executorMessageQueueThread(std::move(jsQueue)),
      m_inspectable(m_executor->isInspectable()) {}

在初始化方法中主要做了如下几件事情:

  • 其中 registry 和 callback 作为入参生成了一个 JsToNativeBridge 类型实例赋值给 m_delegate

  • 使用 JSExecutorFactory_delegate 创建了一个 JSExecutor; 从 RCTxxBridge 中创建的JSExecutorFactory得知此 JSExecutor 就是 JSIExecutor

  • 设置消息队列

额外我们从 NativeToJsBridge 得知一个名叫 ModuleRegistry 的参数;那么 ModuleRegistry 具体有什么作用请见下面的分析:

# ModuleRegistry

moduleRegistry 中包括了所有 native module 信息,即 RCTModuleData;

ModuleRegistry 实例的创建,我们还需要追溯到RCTxxBridge.mm中的实例 Instance 的时候调用了_buildModuleRegistryUnlocked方法;

//RCTxxBridge.mm
- (std::shared_ptr<ModuleRegistry>)_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())];
  };
  // 创建ModuleRegistry;通过_moduleDataByID<RCTModuleData>并创建所有自定义的NativeModule;也就是说 所有原生组件的加载其实把并不是在此处,而是存放在了一个名叫_moduleDataByID数组中
  auto registry = std::make_shared<ModuleRegistry>(
      createNativeModules(_moduleDataByID, self, _reactInstance), moduleNotFoundCallback);

  return registry;
}

// RCTCxxUtils.mm

std::vector<std::unique_ptr<NativeModule>>
createNativeModules(NSArray<RCTModuleData *> *modules, RCTBridge *bridge, const std::shared_ptr<Instance> &instance)
{
  std::vector<std::unique_ptr<NativeModule>> nativeModules;
  for (RCTModuleData *moduleData in modules) {
    // 你可以尝试着打印,你自定义的原生模块是否被加载
      NSLog(@"原生模块: %@",moduleData.name);
    if ([moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
      nativeModules.emplace_back(std::make_unique<CxxNativeModule>(
          instance,
          [moduleData.name UTF8String],
          [moduleData] { return [(RCTCxxModule *)(moduleData.instance) createModule]; },
          std::make_shared<DispatchMessageQueueThread>(moduleData)));
    } else {
      nativeModules.emplace_back(std::make_unique<RCTNativeModule>(bridge, moduleData));
    }
  }
  return nativeModules;
}

# JSIExecutor

前面我们已经将 RCTBridge 的初始化都讲解完成,还剩余最后一个部分,就是 JSIExecutor; 从前面的部分我们知道 NativeToJsBridge持有 JSIExecutor 的实例;(tips: 因为 JSIExecutor 是通过 Executor 工厂创建 的,JSExecutorFactory 是在 RCTxxBridge start 的时候初始化,但是正是调用 createJSExecutor 却是在 NativeToJsBridge 中);

和 Instance、NativeToJsBridge 一样,JSIExecutor 主要用来 Native call JS,但他是比 Instance 和 NativeToJsBridge 更深层次的一个核心类;他会直接和 iOS 的 JavaScriptCore 进行通信

其实 Instance 更像是 NativeToJsBridge 的一个包装类,而 NativeToJsBridge 又更像是 一样,JSIExecutor 的包装类

为什么要这么说呢?我们从如下的三个定义方法就可明白:

// Instance.c
// 初始化bridge
void initializeBridge(
      std::unique_ptr<InstanceCallback> callback,
      std::shared_ptr<JSExecutorFactory> jsef,
      std::shared_ptr<MessageQueueThread> jsQueue,
      std::shared_ptr<ModuleRegistry> moduleRegistry);
  // 从string中加载js
  void loadScriptFromString(
      std::unique_ptr<const JSBigString> string,
      std::string sourceURL,
      bool loadSynchronously);
  void loadRAMBundleFromString(
      std::unique_ptr<const JSBigString> script,
      const std::string &sourceURL);
  void loadRAMBundleFromFile(
      const std::string &sourcePath,
      const std::string &sourceURL,
      bool loadSynchronously);
  // 加载RAM bundle
  void loadRAMBundle(
      std::unique_ptr<RAMBundleRegistry> bundleRegistry,
      std::unique_ptr<const JSBigString> startupScript,
      std::string startupScriptSourceURL,
      bool loadSynchronously);
  // 执行js方法
  void callJSFunction(
      std::string &&module,
      std::string &&method,
      folly::dynamic &&params);
  // 执行js回调
  void callJSCallback(uint64_t callbackId, folly::dynamic &&params);

  // 实验性的方法,注册bundle;建议不要使用
  void registerBundle(uint32_t bundleId, const std::string &bundlePath);


// NativeToJsBridge.h
  // 执行js函数
 void callFunction(
      std::string &&module,
      std::string &&method,
      folly::dynamic &&args);

  // 执行js回调
  void invokeCallback(double callbackId, folly::dynamic &&args);


  void initializeRuntime();
  // 异步加载jsbundle
  void loadBundle(
      std::unique_ptr<RAMBundleRegistry> bundleRegistry,
      std::unique_ptr<const JSBigString> startupCode,
      std::string sourceURL);
  // 同步加载Bundle
  void loadBundleSync(
      std::unique_ptr<RAMBundleRegistry> bundleRegistry,
      std::unique_ptr<const JSBigString> startupCode,
      std::string sourceURL);
  // 注册bundle
  void registerBundle(uint32_t bundleId, const std::string &bundlePath);

//  JSIExecutor.h
  //加载 bundle
  void loadBundle(
      std::unique_ptr<const JSBigString> script,
      std::string sourceURL) override;
  void registerBundle(uint32_t bundleId, const std::string &bundlePath)
      override;
  // 执行方法
  void callFunction(
      const std::string &moduleId,
      const std::string &methodId,
      const folly::dynamic &arguments) override;
  //执行js回调
  void invokeCallback(const double callbackId, const folly::dynamic &arguments)
      override;

通过上面定义的方法,证实了笔者上面所说:

其实 Instance 更像是 NativeToJsBridge 的一个包装类,而 NativeToJsBridge 又更像是 一样,JSIExecutor 的包装类;JSIExecutor 其实才是最核心的

tips: 如果您还心存疑惑,可以查阅下一篇文章ReactNative与iOS原生通信原理解析-JS加载及执行篇 (opens new window)中的执行 JS 代码章节

JSIExecutor 一共有几个关键的属性:


  std::shared_ptr<jsi::Runtime> runtime_;
  std::shared_ptr<ExecutorDelegate> delegate_;
  std::shared_ptr<JSINativeModules> nativeModules_;
  std::once_flag bindFlag_;
  std::unique_ptr<RAMBundleRegistry> bundleRegistry_;

  • <jsi::Runtime> runtime_

    Runtime 类型指针,代表 JS 的运行时。这是一个抽象类,其实际上是由 JSCRuntime 来实现的。JSCRuntime 实现了<jsi::Runtime>接口,提供了创建 JS 上下文的功能,同时可以执行 JS。如下是 JSCRuntime 的 evaluateJavaScript 方法实现:

jsi::Value JSCRuntime::evaluateJavaScript(
    const std::shared_ptr<const jsi::Buffer> &buffer,
    const std::string &sourceURL) {
  std::string tmp(
      reinterpret_cast<const char *>(buffer->data()), buffer->size());
  JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str());
  JSStringRef sourceURLRef = nullptr;
  if (!sourceURL.empty()) {
    sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str());
  }
  JSValueRef exc = nullptr;
  // JSEvaluateScript是JavaScriptCore中用于执行js代码的工具
  JSValueRef res =
      JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc);
  JSStringRelease(sourceRef);
  if (sourceURLRef) {
    JSStringRelease(sourceURLRef);
  }
  checkException(res, exc);
  return createValue(res);
}
  • <ExecutorDelegate> delegate_

ExecutorDelegate 类型的指针,这里的 ExecutorDelegate 是抽象类,实际是由JsToNativeBridge实现的。也即JSIExecutor引用了JsToNativeBridge实例。

NativeToJsBridge 使用 registry 创建了 JsToNativeBridge 实例m_delegate;又使用 m_delegate 创建 JSIExecutor;

// NativeToJsBridge.cpp
NativeToJsBridge::NativeToJsBridge(
    JSExecutorFactory *jsExecutorFactory,
    std::shared_ptr<ModuleRegistry> registry,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<InstanceCallback> callback)
    : m_destroyed(std::make_shared<bool>(false)),
      m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
      m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
      m_executorMessageQueueThread(std::move(jsQueue)),
      m_inspectable(m_executor->isInspectable()) {}
  • <JSINativeModules> nativeModules_

JSINativeModules 由上层传入的 ModuleRegistry 构造而成;而 ModuleRegistry 又是在 RCTxxBridge 中构造而成。

JSINativeModules 一共有两个方法:getModulecreateModule

// JSINativeModules.cpp
Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name) {
  if (!m_moduleRegistry) {
    return nullptr;
  }

  std::string moduleName = name.utf8(rt);

  const auto it = m_objects.find(moduleName);
  if (it != m_objects.end()) {
    return Value(rt, it->second);
  }
  // 调用createModule方法
  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;
  }

  auto result =
      m_objects.emplace(std::move(moduleName), std::move(*module)).first;
  return Value(rt, result->second);
}


folly::Optional<Object> JSINativeModules::createModule(
    Runtime &rt,
    const std::string &name) {
  bool hasLogger(ReactMarker::logTaggedMarker);
  if (hasLogger) {
    ReactMarker::logTaggedMarker(
        ReactMarker::NATIVE_MODULE_SETUP_START, name.c_str());
  }
  // runtime获取名为__fbGenNativeModule的函数指针赋值给m_genNativeModuleJS
  // JS端的函数__fbGenNativeModule调用最终就会走到这里
  if (!m_genNativeModuleJS) {
    m_genNativeModuleJS =
        rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
  }

  auto result = m_moduleRegistry->getConfig(name);
  if (!result.hasValue()) {
    return folly::none;
  }
   // 调用m_genNativeModuleJS函数,即__fbGenNativeModule
  Value moduleInfo = m_genNativeModuleJS->call(
      rt,
      valueFromDynamic(rt, result->config),
      static_cast<double>(result->index));
  CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";

  folly::Optional<Object> module(
      moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));

  if (hasLogger) {
    ReactMarker::logTaggedMarker(
        ReactMarker::NATIVE_MODULE_SETUP_STOP, name.c_str());
  }

  return module;
}

看到 JSINativeModules 的两个方法,好想打开了一扇大门;但是又有一些疑惑?

  • createModule在 getModule 中调用,那么 getModule 再哪里被使用?
  • __fbGenNativeModule是什么?难道 js 和 RN 是通过__fbGenNativeModule进行通信的吗?

# NativeModuleProxy

问题 1

createModule在 getModule 中调用,那么 getModule 再哪里被使用?

getModule 方法上按住 CMD 然后点击 caller;我们发现全局唯一使用的地方在 JSIExecutor 中的 NativeModuleProxy 中:

// JSIExecutor.cpp

class JSIExecutor::NativeModuleProxy : public jsi::HostObject {
 public:
  // 构造函数 JSIExecutor实例作为NativeModuleProxy构造函数的入参
  NativeModuleProxy(std::shared_ptr<JSINativeModules> nativeModules)
      : weakNativeModules_(nativeModules) {}
 // NativeModuleProxy 的 get方法 用于获取native module信息
  Value get(Runtime &rt, const PropNameID &name) override {
    return nativeModules->getModule(rt, name);
  }

  void set(Runtime &, const PropNameID &, const Value &) override {
    throw std::runtime_error(
        "Unable to put on NativeModules: Operation unsupported");
  }

 private:
  std::weak_ptr<JSINativeModules> weakNativeModules_;
};

那么 NativeModuleProxy 这个 java 类又是在哪里使用的呢?全局搜索 NativeModuleProxy,你会发现只有一个地方再使用 NativeModuleProxy,就是 JSIExecutorinitializeRuntime 方法,源码如下:


void JSIExecutor::initializeRuntime() {
  SystraceSection s("JSIExecutor::initializeRuntime");
  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));
// ...
}

JSIExecutorinitializeRuntime的调用链为:

RCTxxBridge.start->Instance.initializeBridge->NativeToJSBridge.initializeRuntime->JSIExecutor.initializeRuntime;说明在 runtime 时设置 nativeModuleProxy 是在初始化的时候执行的。

上面的代码只是列了nativeModuleProxy; 其实还有很多runtime_->global().setProperty( //... );

runtime 是一个 JSCRuntime 类型对象,通过调用 rumtime_->global()获得一个全局的 global 对象。然后又通过 setProperty 方法给 global 对象设置了一个名为 nativeModuleProxy 的对象。日后(JS 侧的)global 对象通过"nativeModuleProxy"这个名字即可访问到(native 侧的)NativeModuleProxy。

// react-native/Libraries/BatchedBridge/NativeModules.js
let NativeModules: { [moduleName: string]: Object, ... } = {};
if (global.nativeModuleProxy) {
  // 使用nativeModuleProxy
  NativeModules = global.nativeModuleProxy;
} else if (!global.nativeExtensions) {
  const bridgeConfig = global.__fbBatchedBridgeConfig;

  const defineLazyObjectProperty = require("../Utilities/defineLazyObjectProperty");
  (bridgeConfig.remoteModuleConfig || []).forEach(
    (config: ModuleConfig, moduleID: number) => {
      // Initially this config will only contain the module name when running in JSC. The actual
      // configuration of the module will be lazily loaded.
      const info = genModule(config, moduleID);
      if (!info) {
        return;
      }

      if (info.module) {
        NativeModules[info.name] = info.module;
      }
      // If there's no module config, define a lazy getter
      else {
        defineLazyObjectProperty(NativeModules, info.name, {
          get: () => loadModule(info.name, moduleID),
        });
      }
    }
  );
}

没错,这一切都不是巧合;JS 侧的 NativeModules == JS 侧的 global.nativeModuleProxy == native 侧 NativeModuleProxy = native 侧所有原生及自定义的 Native Module

# __fbGenNativeModule

我们还没回答第二个问题:

__fbGenNativeModule是什么?难道 js 和 RN 是通过__fbGenNativeModule进行通信的吗?

在 native 中通过 runtime 的方式获取到 js 的全局对象__fbGenNativeModule;

// JSINativeModules.cpp

folly::Optional<Object> JSINativeModules::createModule(
    Runtime &rt,
    const std::string &name) {
  bool hasLogger(ReactMarker::logTaggedMarker);
  if (hasLogger) {
    ReactMarker::logTaggedMarker(
        ReactMarker::NATIVE_MODULE_SETUP_START, name.c_str());
  }
  // runtime获取名为__fbGenNativeModule的函数指针赋值给m_genNativeModuleJS
  // JS端的函数__fbGenNativeModule调用最终就会走到这里
  if (!m_genNativeModuleJS) {
    m_genNativeModuleJS =
        rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
  }

   // 调用m_genNativeModuleJS函数,即__fbGenNativeModule
  Value moduleInfo = m_genNativeModuleJS->call(
      rt,
      valueFromDynamic(rt, result->config),
      static_cast<double>(result->index));
  CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";

  folly::Optional<Object> module(
      moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));

  return module;
}

那我们就来看看 js 端是否有__fbGenNativeModule这个全局函数:

// react-native/Libraries/BatchedBridge/NativeModules.js

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);

      const methodType = isPromise ? "promise" : isSync ? "sync" : "async";
      module[methodName] = genMethod(moduleID, methodID, methodType);
    });

  Object.assign(module, constants);

  if (module.getConstants == null) {
    module.getConstants = () => constants || Object.freeze({});
  } else {
  }

  return { name: moduleName, module };
}

// export this method as a global so we can call it from native
global.__fbGenNativeModule = genModule;

还真别说在 JS 端真存在一个__fbGenNativeModule的全局方法;其主要作用就是方便 native 调用 js 代码对应 module 下的方法。

回答上面的问题:__fbGenNativeModule的主要作用是方便 native 侧调用 js 的模块下的方法;也就是native to JS

# 总结

前面学习了太多新的知识,这里我们使用一个时序图来总结如上各个主要类的执行顺序:

;

在 RN 的整个初始化中一共做了如下几件事情:

  1. 初始化了 RCTBridge;并在 RCTxxBridge 中开启 JS 执行线程;绑定了一个 runRunLoop 方法,保证所有的 js 代码都在此线程执行

  2. 初始化所有的 native modules(包括自定义的,RN 自带的以及通过 options 参数传递的);

  3. 在 JS 线程中利用 JSExecutorFactory Instance 并创建了一个 NativeToJsBridge 实例;并利用 NativeToJSBridge 实例创建出 JSIExecutor;其中 JSIExecutor 才是整个 JS 加载初始化及执行的核心;

  4. 在初始化上述的 JSIExecutor 的同时,异步加载 JS 代码并等待初始化完成和 JS 代码加载完成,就开始执行 JS 代码。

本文由于篇幅过长,暂未讲述 JS 代码如何加载及执行,详情请见下一篇 ReactNative与iOS原生通信原理解析-JS加载及执行篇 (opens new window) 去理解js的加载和执行的整个流程。

ReactNative 与 iOS 原生通信原理解析系列

【未经作者允许禁止转载】 Last Updated: 5/15/2021, 7:18:39 AM