美文网首页程序员
react-native源码——启动

react-native源码——启动

作者: 请叫我啊亮 | 来源:发表于2017-10-23 18:05 被阅读139次

rn应用启动时,利用RCTBundleURLProvider获取到js端入口文件index.ios.js的url地址,再生成程序根视图RCTRootView。RCTRootView的创建方法

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(NSDictionary *)initialProperties
                    launchOptions:(NSDictionary *)launchOptions
{
  RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL
                                            moduleProvider:nil
                                             launchOptions:launchOptions];
  return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}

生成一个bridge,在调用initWithBridge方法,在这个方法中注册一系列通知,用来监听js文件的加载过程,在文件加载完成后刷新视图等。RCTRootView内部有属性强引用bridge对象。

bridge的创建方法主要调用的是如下

- (void)setUp
{
  ... 省略
  _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
  self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
  [self.batchedBridge start];
}

bridge没做啥实事,主要作用就是生成了RCTBatchedBridge对象,这是RCTBridge的一个子类,并强引用了它。
具体干活的对象是RCTBatchedBridge。看它的start方法。

- (void)start
{
  [[NSNotificationCenter defaultCenter]
    postNotificationName:RCTJavaScriptWillStartLoadingNotification
    object:_parentBridge userInfo:@{@"bridge": self}];
  RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge setUp]", nil);
  dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);

  ---------- 标记@1
  dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
  dispatch_group_enter(initModulesAndLoadSource);
  __weak RCTBatchedBridge *weakSelf = self;
  __block NSData *sourceCode;
    ---------- 标记@2
  [self loadSource:^(NSError *error, NSData *source, __unused int64_t sourceLength) {
    if (error) {
      RCTLogWarn(@"Failed to load source: %@", error);
      dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf stopLoadingWithError:error];
      });
    }
    sourceCode = source;
    dispatch_group_leave(initModulesAndLoadSource);
  } onProgress:^(RCTLoadingProgress *progressData) {
#if RCT_DEV && __has_include("RCTDevLoadingView.h")
    RCTDevLoadingView *loadingView = [weakSelf moduleForClass:[RCTDevLoadingView class]];
    [loadingView updateProgress:progressData];
#endif
  }];

   ---------- 标记@3
  [self initModulesWithDispatchGroup:initModulesAndLoadSource];

  RCTPerformanceLogger *performanceLogger = self->_performanceLogger;
  __block NSString *config;
  dispatch_group_enter(initModulesAndLoadSource);
  dispatch_async(bridgeQueue, ^{
    
     ---------- 标记@4
    dispatch_group_t setupJSExecutorAndModuleConfig = dispatch_group_create();

     ---------- 标记@5
    dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
      [performanceLogger markStartForTag:RCTPLJSCExecutorSetup];
      [weakSelf setUpExecutor];
      [performanceLogger markStopForTag:RCTPLJSCExecutorSetup];
    });

    ---------- 标记@6
    dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
      if (weakSelf.valid) {
        RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge moduleConfig", nil);
        [performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
        config = [weakSelf moduleConfig];
        [performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
        RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
      }
    });
     ---------- 标记@7
    dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
      [performanceLogger markStartForTag:RCTPLNativeModuleInjectConfig];
      [weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) {
        [performanceLogger markStopForTag:RCTPLNativeModuleInjectConfig];
        if (error) {
          RCTLogWarn(@"Failed to inject config: %@", error);
          dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf stopLoadingWithError:error];
          });
        }
      }];
      dispatch_group_leave(initModulesAndLoadSource);
    });
  });
   ---------- 标记@8
  dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
    RCTBatchedBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      [strongSelf executeSourceCode:sourceCode];
    }
  });
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}

@1: 创建外层的队列组A
@2: 队列组A中的第一个任务,加载我们写的那些js源码文件
@3: 同步的形式,将OC中通过宏RCT_EXPORT_MODULE导出的所有模块信息,例如模块名字、模块对应的RCTModuleData实例等添加到属性_moduleDataByID等之中。同时初始化所有不能懒加载的模块实例。
@4: 创建队列组B,该队列组用于A的一个子任务之中。意思就是队列组A有一个任务,这个任务内部又创建了一个队列组B,B队列组又包含了两个任务,分别是@5和@6。
@5: 队列组B的子任务,初始化js执行器_javaScriptExecutor。
@6: 将@3已经装载的模块信息,按照一定格式组装结构。
@7: @5、@6完成,将模块信息注入js执行上下文中,这样js代码就可以调用了。
@8: @5、@6完成后,未等@7完成,队列组即已经退出,执行@8操作。执行上面加载的js代码,通知RootView页面刷新等。

细究部分:
@3: initModulesWithDispatchGroup方法的实现

- (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
  ... 抽取主要代码
  for (Class moduleClass in RCTGetModuleClasses()) {
    NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
    RCTModuleData *moduleData = moduleDataByName[moduleName];
    moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass
                                                     bridge:self];
    moduleDataByName[moduleName] = moduleData;
    [moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
  }
  _moduleDataByID = [moduleDataByID copy];
  _moduleDataByName = [moduleDataByName copy];
  _moduleClassesByID = [moduleClassesByID copy];

  [self prepareModulesWithDispatchGroup:dispatchGroup];
}

方法RCTGetModuleClasses()拿到所有OC中通过RCT_EXPORT_MODULE(js_name)导出的模块名字(若宏后面没写名字,则默认为文件名字),遍历,包装成RCTModuleData类型的数据,存到RCTBatchedBridge的三个属性_moduleDataByID、_moduleDataByName、_moduleClassesByID之中。
@3内部方法prepareModulesWithDispatchGroup实现

- (void)prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
  ... 抽取主要代码
  for (RCTModuleData *moduleData in _moduleDataByID) {
    if (moduleData.requiresMainQueueSetup || moduleData.hasConstantsToExport) {
      dispatch_block_t block = ^{
        if (self.valid) {
          [self->_performanceLogger appendStartForTag:RCTPLNativeModuleMainThread];
          (void)[moduleData instance];             --------- 标记p
          [moduleData gatherConstants];            --------- 标记q
          [self->_performanceLogger appendStopForTag:RCTPLNativeModuleMainThread];
        }
      };
      dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
}}

意思是找出所有模块中需要预先实例化的模块,初始化它。具体是如果当前模块实现了init或者constantsToExport方法,则上面属性requiresMainQueueSetup和hasConstantsToExport属性会为YES,进入标记p和标记q处,实例化模块。
一般模块采用的是懒加载模式,当js中导入时才加载,但是上面的这两种情况需要在程序启动时就加载,文档意思说是防止死锁,但我没想到应用场景,自己项目中注释掉p和q,也是能正常工作的。这样发现的问题就是一些系统自己使用的模块没有初始化而无法工作,例如RCTDevLoadingView。

相关文章

网友评论

    本文标题:react-native源码——启动

    本文链接:https://www.haomeiwen.com/subject/xrmauxtx.html