iOS中的RN的启动流程

578 阅读4分钟

iOS中的RN的启动流程

1. 启动

RN的官方demo, 启动时, 会进行如下代码调用:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // 创建 bridge
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  // 默认的 module name 是 AwesomeTSProject, 也就是 Target名称
  UIView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:"AwesomeTSProject" initialProperties:nil];

  ...
}

过程中, 有如下关键步骤:

  1. bridge创建, 并初始化
  2. 使用bridge作为参数, 创建RCTRootView
  3. RCTRootView对象设置到window.rootviewController.view

2. RN中Bridge的初始化

RN中拥有几个bridge:

  1. RCTBridge
  2. RCTBridge的方法- (Class)bridgeClass返回[RCTCxxBridge class]
  3. RCTBridge的成员变量batchedBridge

上面的RCTBridge初始化方法执行过程中关键的信息在-setUp中执行:

- (void)setUp {
    NSURL *previousDelegateURL = _delegateBundleURL; // 通过delegate 从外部获取的 bundleURL
    _delegateBundleURL = [self.delegate sourceURLForBridge:self];
    if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
        _bundleURL = _delegateBundleURL;
    }

    _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
    
    self.batchedBridge = [[RCTCxxBridge alloc] initWithParentBridge:self];
    [self.batchedBridge start];
}

以上方法, 删除了一些干扰内容, 完成以下几个事情:

  1. 记录bundleURL, 这个是js bundle的路径
  2. 创建RCTCxxBridge对象, 用batchedBridge持有, 初始化方法使用-initWithParentBridge
  3. 启动RCTCxxBridge-start方法 - 正式初始化 RN Bridge 桥阶层

3. RN中RCTCxxBridge的初始化

- (instancetype)initWithParentBridge:(RCTBridge *)bridge {
  if ((self = [super initWithDelegate:bridge.delegate
                            bundleURL:bridge.bundleURL
                       moduleProvider:bridge.moduleProvider
                        launchOptions:bridge.launchOptions])) {
    /// RCTBridge 很多接口会使用parentBridge
    _parentBridge = bridge;

    /// 性能服务
    _performanceLogger = [bridge performanceLogger];

    _valid = YES; // 当 handleError: 或者 invalidate 时, _valid = NO
    _loading = YES;
    _moduleRegistryCreated = NO; // reactInstance 持有的 RN 注册中心是否初始化
    _pendingCalls = [NSMutableArray new]; //js调用还没有返回结果的
    _displayLink = [RCTDisplayLink new]; // 用来通知那些需要实时监听屏幕帧率的 native module 辅助类
    ///  _moduleDataByName/_moduleClassesByID/moduleDataByID 是三个 native module 的容器
    _moduleDataByName = [NSMutableDictionary new];
    _moduleClassesByID = [NSMutableArray new];
    _moduleDataByID = [NSMutableArray new];
    
    /// ModuleRegistry 用来提供一些快速查询 bridge 中的其他module的API
    _objCModuleRegistry = [RCTModuleRegistry new];
    [_objCModuleRegistry setBridge:self];
    
    /// BundleManager 用来提供一些快速查询 bridge 管理的 bundleURL 的API
    _bundleManager = [RCTBundleManager new]; 
    [_bundleManager setBridge:self]; 
    
    /// RCTViewRegistry 用来提供一些快速查询 view相关的 的API
    _viewRegistry_DEPRECATED = [RCTViewRegistry new];
    [_viewRegistry_DEPRECATED setBridge:self];
    
    /// RCTCallableJSModules 用来提供 JS 能调用的 module
    _callableJSModules = [RCTCallableJSModules new];
    [_callableJSModules setBridge:self];
    
    /// 全局静态对象 RCTCurrentBridgeInstance 被设置
    [RCTBridge setCurrentBridge:self];

    /// 注册内存告警
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  }
  return self;
}

RCTCxxBridge的初始化, 有以下三个内容:

  1. 基础的状态标记: valid, loading, moduleRegistryCreated
  2. native module 的容器: _moduleDataByName, _moduleClassesByID, _moduleDataByID
  3. 包装工具API: RCTModuleRegistry, BundleManager, RCTViewRegistry, RCTCallableJSModules
  4. 设置自己是当前全局的静态对象: RCTCurrentBridgeInstance

4. batchedBridge 启动的流程 - start方法

RN的Bridge的启动流程中, 最核心的代码在[self.batchedBridge start];. 具体的流程文字版如下:

  1. 全局通知RCTJavaScriptWillStartLoadingNotification
  2. 创建并启动JS Thread, 注意保活, 后续大量的 js 语句都会在JS Thread 中执行.
  3. Native Moduel init, 分成以下三个部分, 可以认为调用的是一个方法-[RCTCxxBridge initModulesWithDispatchGroup:]:
    1. 强制注册提供的moduleProvider block中的native module --> 同步执行
      1. 从delegate中的appExtraModules or moduleProvider()中的NSArray<id<RCTBridgeModule>>
      2. id<RCTBridgeModule>的初始化, 实际将 module 包装成RCTModuleData *
      3. 将生成的RCTModuleData *加入到全局的 _moduleDataByName字典中.
    2. DispatchGroup中初始化所有的RCTModuleClasses中注册过的module, 大量的module注册!!! --> 同步执行
    3. DEBUG专用 -- 忽略!!!extra lazy module的注册, 这部分通常在DEBUG环境中用来增加一些调试使用的module
  4. 创建一个空的对象 _reactInstance, 创建JS环境工厂 -- std::shared_ptr<JSExecutorFactory> executorFactory, 这个 executorFactory 是后面真正初始化js相关环境的必要组件
  5. TurboModule 先忽略
  6. 使用DispatchGroup中, 异步在JS Thread中执行_initializeBridge, 其实就是初始化_reactInstance, 注意, 会添加到 DispatchGroup 中
  7. 使用preapreBridge DispatchGroup中, 异步加载js bundle, 注意, 会添加到 DispatchGroup中
  8. 异步等待DispatchGroup所有完成!!! 然后正式执行executeSourceCode, 也就是执行js bundle里面的js代码!!!

代码注释版如下:

- (void)start {
    // 1. RCTJavaScriptWillStartLoadingNotification 消息通知, RCTRootView 会接受
    [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification object:_parentBridge userInfo:@{@"bridge" : self}];
    
    // 2.提前设置并开启JS线程
    _jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
    _jsThread.name = RCTJSThreadName; //  @"com.facebook.react.JavaScript";
    _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
    [_jsThread start];

    // 3. 大量的 native module 初始化的过程
    dispatch_group_t prepareBridge = dispatch_group_create();
    
    // 3.1 `native modules` 第一趴, 主要是在初始化 RCTCxxBridge, 以及通过delegate 获取外部的 Native Module Instance
    [self registerExtraModules];
    
    // 3.2 重点:注册所遇的自定义Native Module, 注意 lazilyDiscovered = NO
    // 所有的 ModuleClasses 维护在全局的静态 ModuleClasses 中, 调用下面方法对所有通过 +load注册到 RCTModuleClasses 中的 Module 进行初始化!!!    
    (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];

    // 3.3 初始化所有懒加载的native module
    [self registerExtraLazyModules];

    // 这里 module 的初始化结束!!!


    // 4. reset reactInstance, 准备executor factory, 为 js 环境初始化做准备

    _reactInstance.reset(new Instance);
    std::shared_ptr<JSExecutorFactory> executorFactory; //用的 shared_ptr 服务
    auto installBindings = RCTJSIExecutorRuntimeInstaller(nullptr);
    executorFactory = std::make_shared<JSCExecutorFactory>(installBindings);
    
    // 5.  利用`executorFactory`来`initializeBridge`, 实际 JSE 环境初始化
    
    dispatch_group_enter(prepareBridge);
    __weak RCTCxxBridge *weakSelf = self;
    [self ensureOnJavaScriptThread:^{
        [weakSelf _initializeBridge:executorFactory];
        dispatch_group_leave(prepareBridge);
    }];

    //6. 异步加载js bundle, 里面有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:nil];

    // 7. 调度组等待, native module 实例化完成, js bundle 加载完成, 执行 js 代码!!!
    dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
        RCTCxxBridge *strongSelf = weakSelf;
        if (sourceCode && strongSelf.loading) {
            [strongSelf executeSourceCode:sourceCode sync:NO];
        }
    });
}