美文网首页
React Native源代码分析--IOS客户端启动流程(从O

React Native源代码分析--IOS客户端启动流程(从O

作者: huangjun0 | 来源:发表于2019-05-28 11:55 被阅读0次

    从RN的架构来看,之所以它在java和js层中间使用一层C++来实现桥接,主要是为了让RN的代码在两个平台中尽量复用。IOS中OC可以和C++任意的相互调用,而在Android中,Java和C++之间也可以通过JNI来进行相互的调用。在早期的RN版本中C++的代码还不是很多,越到后来C++层的代码占比越大,这说明facebook逐步在将所有的可以共用的代码逐渐迁移到C++中,而不需要oc和java写两套。当然由于平台的差异性,很多东西还是得分开。在RN源码的文件夹的组织结构中可以看到,其中React文件夹下就是使用OC编写的IOS端的代码,ReactAndroid是用Java写的Android的,而ReactCommon则是公用的C++代码。
    IOS的很多流程其实和Android端的类似,主要流程如下:
    React Native各个版本间的代码差异不小,现在网上流行的代码解读,基本上都是0.4X或者以前的,虽然从原理上讲大同小异,但是在实现过程中却又不小的变化。本文就以一个基于0.5X版本的官方示例Awesomeproject来看一下它从启动到最终绘制的整个过程。

    启动流程

    首先,AppDelegate获取js文件的url地址
    其次,创建RCTRootView
    最后,将RCTRootView设置为rootViewController的rootView显示
    其中最主要的东西都是在创建RootView过程中实现的

      RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                          moduleName:@"AwesomeProject"
                                                   initialProperties:nil
                                                       launchOptions:launchOptions];
    

    继续跟进下去发现,RCTRootView的创建过程主要是首先创建RTCBridge,然后使用RTCBridge初始化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];
    }
    

    这个方法首先创建RCTBridge,然后初拥bridge初始化。
    我们首先分析RCTBridge的创建过程:
    我们跟踪RTCBridge的调用他的最终实现如下

    - (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;
    }
    
    setUp方法
    - (void)setUp
    {
      RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);
    
      _performanceLogger = [RCTPerformanceLogger new];
      [_performanceLogger markStartForTag:RCTPLBridgeStartup];
      [_performanceLogger markStartForTag:RCTPLTTI];
    
      Class bridgeClass = self.bridgeClass;
    
      #if RCT_DEV
      RCTExecuteOnMainQueue(^{
        RCTRegisterReloadCommandListener(self);
      });
      #endif
    
      // Only update bundleURL from delegate if delegate bundleURL has changed
      NSURL *previousDelegateURL = _delegateBundleURL;
      _delegateBundleURL = [self.delegate sourceURLForBridge:self];
      if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
        _bundleURL = _delegateBundleURL;
      }
    
      // Sanitize the bundle URL
      _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
    
      self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
      [self.batchedBridge start];
    
      RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
    }
    

    其中我们可以看到bridgeClass的类型是RCTCxxBridge

    - (Class)bridgeClass
    {
      return [RCTCxxBridge class];
    }
    

    因此setUp方法中的 [self.batchedBridge start];实际调用的是[RCTCxxBridge start]方法。start 方法很长,我们来详细分析一下:

    - (void)start
    {
      RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil);
    //发送RCTJavaScriptWillStartLoadingNotification的消息
      [[NSNotificationCenter defaultCenter]
        postNotificationName:RCTJavaScriptWillStartLoadingNotification
        object:_parentBridge userInfo:@{@"bridge": self}];
    
      // Set up the JS thread early,创建JS线程
      _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();
    
      [_performanceLogger markStartForTag:RCTPLNativeModuleInit];
    
      [self registerExtraModules];
      // Initialize all native modules that cannot be loaded lazily, 初始化Native module
      (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
      [self registerExtraLazyModules];
    
      [_performanceLogger markStopForTag:RCTPLNativeModuleInit];
    
      // This doesn't really do anything.  The real work happens in initializeBridge.
      _reactInstance.reset(new Instance);
    
      __weak RCTCxxBridge *weakSelf = self;
    
      // Prepare executor factory (shared_ptr for copy into block)
      std::shared_ptr<JSExecutorFactory> executorFactory;
      if (!self.executorClass) {
        if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
          id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>) self.delegate;
          executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
        }
        if (!executorFactory) {
          executorFactory = std::make_shared<JSIExecutorFactory>(
              makeJSCRuntime(),
              [](const std::string &message, unsigned int logLevel) {
                  _RCTLogJavaScriptInternal(
                      static_cast<RCTLogLevel>(logLevel),
                      [NSString stringWithUTF8String:message.c_str()]);
              }, nullptr);
        }
      } else {
        id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
        executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
          if (error) {
            [weakSelf handleError:error];
          }
        }));
      }
    
      // 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);
      }];
    
      // 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 && __has_include("RCTDevLoadingView.h")
        // Note: RCTDevLoadingView should have been loaded at this point, so no need to allow lazy loading.
        RCTDevLoadingView *loadingView = [weakSelf moduleForName:RCTBridgeModuleNameForClass([RCTDevLoadingView class])
                                           lazilyLoadIfNecessary:NO];
        [loadingView updateProgress:progressData];
    #endif
      }];
    
      // 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, @"");
    }
    

    代码很长,我们一步步看,他实现的功能主要是:
    1.创建JS线程
    2.初始化NativeModule
    [self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
    其中,RCTGetModuleClasses() 会返回一个NSArray RCTModuleClasses,而RCTModuleClasses的创建函数是

    void RCTRegisterModule(Class moduleClass)
    {
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        RCTModuleClasses = [NSMutableArray new];
        RCTModuleClassesSyncQueue = dispatch_queue_create("com.facebook.react.ModuleClassesSyncQueue", DISPATCH_QUEUE_CONCURRENT);
      });
    
      RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
                @"%@ does not conform to the RCTBridgeModule protocol",
                moduleClass);
    
      // Register module
      dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
        [RCTModuleClasses addObject:moduleClass];
      });
    }
    

    而源码中有对整个函数的宏定义:

    define RCT_EXPORT_MODULE(js_name) \

    RCT_EXTERN void RCTRegisterModule(Class); \

    • (NSString *)moduleName { return @#js_name; } \
    • (void)load { RCTRegisterModule(self); }
      因此所有通过RCT_EXPORT_MODULE()定义的类(也就是所有对JS层开放的类)都会在初始化的时候加入到RCTModuleClasses的类中。
      最终会调用如下的方法返回一个NSArray<RCTModuleData *> 。其中它会遍历所有的RN module然后创建各自的moduleData。而RCTModuleData中会有Module的所有的对外暴露的方法、属性和一些其他标识位(implementsBatchDidComplete、implementsPartialBatchDidFlush等)。
    - (NSArray<RCTModuleData *> *)_registerModulesForClasses:(NSArray<Class> *)moduleClasses
                                            lazilyDiscovered:(BOOL)lazilyDiscovered
    {
      RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways,
                              @"-[RCTCxxBridge initModulesWithDispatchGroup:] autoexported moduleData", nil);
    
      NSArray *moduleClassesCopy = [moduleClasses copy];
      NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
      for (Class moduleClass in moduleClassesCopy) {
        if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
          continue;
        }
        NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
    
        // Check for module name collisions
        RCTModuleData *moduleData = _moduleDataByName[moduleName];
        if (moduleData) {
          if (moduleData.hasInstance || lazilyDiscovered) {
            // Existing module was preregistered, so it takes precedence
            continue;
          } else if ([moduleClass new] == nil) {
            // The new module returned nil from init, so use the old module
            continue;
          } else if ([moduleData.moduleClass new] != nil) {
            // Both modules were non-nil, so it's unclear which should take precedence
            RCTLogError(@"Attempted to register RCTBridgeModule class %@ for the "
                        "name '%@', but name was already registered by class %@",
                        moduleClass, moduleName, moduleData.moduleClass);
          }
        }
    
        // Instantiate moduleData
        // TODO #13258411: can we defer this until config generation?
        moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
    
        _moduleDataByName[moduleName] = moduleData;
        [_moduleClassesByID addObject:moduleClass];
        [moduleDataByID addObject:moduleData];
      }
      [_moduleDataByID addObjectsFromArray:moduleDataByID];
    
      RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
    
      return moduleDataByID;
    }
    

    3.创建<JSIExecutorFactory> executorFactory
    4.在js线程初始化通讯桥
    [weakSelf _initializeBridge:executorFactory]->Instance::initializeBridge->nativeToJsBridge:: nativeToJsBridge->jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)->JSIExecutorFactory::createJSExecutor->JSIExecutor::JSIExecutor

    JSIExecutor::JSIExecutor(
        std::shared_ptr<jsi::Runtime> runtime,
        std::shared_ptr<ExecutorDelegate> delegate,
        Logger logger,
        const JSIScopedTimeoutInvoker& scopedTimeoutInvoker,
        RuntimeInstaller runtimeInstaller)
        : runtime_(runtime),
          delegate_(delegate),
          nativeModules_(delegate ? delegate->getModuleRegistry() : nullptr),
          logger_(logger),
          scopedTimeoutInvoker_(scopedTimeoutInvoker),
          runtimeInstaller_(runtimeInstaller) {
      runtime_->global().setProperty(
          *runtime, "__jsiExecutorDescription", runtime->description());
    }
    

    5.加载js文件

    • (void)loadSource:(RCTSourceLoadBlock)onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
      6.运行js文件
      [strongSelf executeSourceCode:sourceCode sync:NO];->[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion]->[self executeApplicationScript:script url:url async:YES]->self->reactInstance->loadScriptFromString(std::make_unique<NSDataBigString>(script),
      sourceUrlStr.UTF8String, !async);->Instance::loadScriptFromString->loadApplication->nativeToJsBridge
      ->loadApplication->JSIExecutor::loadApplicationScript->runtime
      ->evaluateJavaScript

    创建完RCTBridge之后,就是初始化:
    [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties]-> [self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];

    - (void)bundleFinishedLoading:(RCTBridge *)bridge
    {
      RCTAssert(bridge != nil, @"Bridge cannot be nil");
      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;
      }
    }
    

    它主要是两个功能,
    一是创建RCTRootContentView
    二是加载应用
    我们先看创建RCTRootContentView

    - (instancetype)initWithFrame:(CGRect)frame
                           bridge:(RCTBridge *)bridge
                         reactTag:(NSNumber *)reactTag
                   sizeFlexiblity:(RCTRootViewSizeFlexibility)sizeFlexibility
    {
      if ((self = [super initWithFrame:frame])) {
        _bridge = bridge;
        self.reactTag = reactTag;
        _sizeFlexibility = sizeFlexibility;
        _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
        [_touchHandler attachToView:self];
        [_bridge.uiManager registerRootView:self];
      }
      return self;
    }
    

    在view上加上一个手势识别器,然后在bridge.uiManager中注册自己。
    然后,再看加载应用:[self runApplication:bridge];->[bridge enqueueJSCall:@"AppRegistry"
    method:@"runApplication"
    args:@[moduleName, appParameters]
    completion:NULL];->strongSelf->reactInstance->callJSFunction([module UTF8String], [method UTF8String],
    convertIdToFollyDynamic(args ?: @[]));->ativeToJsBridge
    ->callFunction(std::move(module), std::move(method),std::move(params));->executor->callFunction->callFunctionReturnFlushedQueue
    ->call()-> callFunctionReturnFlushedQueue(module: string, method: string, args: any[])
    执行完后,执行回调方法callNativeModules(ret, true);- >JSIExecutor::callNativeModules->delegate_->callNativeModules->m_callback->onBatchComplete();最终回调给native模块

    相关文章

      网友评论

          本文标题:React Native源代码分析--IOS客户端启动流程(从O

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