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];
...
}
过程中, 有如下关键步骤:
bridge创建, 并初始化- 使用
bridge作为参数, 创建RCTRootView - 将
RCTRootView对象设置到window.rootviewController.view
2. RN中Bridge的初始化
RN中拥有几个bridge:
RCTBridgeRCTBridge的方法- (Class)bridgeClass返回[RCTCxxBridge class]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];
}
以上方法, 删除了一些干扰内容, 完成以下几个事情:
- 记录
bundleURL, 这个是js bundle的路径 - 创建
RCTCxxBridge对象, 用batchedBridge持有, 初始化方法使用-initWithParentBridge - 启动
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的初始化, 有以下三个内容:
- 基础的状态标记: valid, loading, moduleRegistryCreated
- native module 的容器:
_moduleDataByName, _moduleClassesByID, _moduleDataByID - 包装工具API:
RCTModuleRegistry, BundleManager, RCTViewRegistry, RCTCallableJSModules - 设置自己是当前全局的静态对象:
RCTCurrentBridgeInstance
4. batchedBridge 启动的流程 - start方法
RN的Bridge的启动流程中, 最核心的代码在[self.batchedBridge start];. 具体的流程文字版如下:
- 全局通知
RCTJavaScriptWillStartLoadingNotification - 创建并启动
JS Thread, 注意保活, 后续大量的 js 语句都会在JS Thread中执行. Native Moduel init, 分成以下三个部分, 可以认为调用的是一个方法-[RCTCxxBridge initModulesWithDispatchGroup:]:- 强制注册提供的
moduleProvider block中的native module--> 同步执行- 从delegate中的
appExtraModulesormoduleProvider()中的NSArray<id<RCTBridgeModule>> id<RCTBridgeModule>的初始化, 实际将 module 包装成RCTModuleData *- 将生成的
RCTModuleData *加入到全局的_moduleDataByName字典中.
- 从delegate中的
- 在
DispatchGroup中初始化所有的RCTModuleClasses中注册过的module, 大量的module注册!!! --> 同步执行 - DEBUG专用 -- 忽略!!!
extra lazy module的注册, 这部分通常在DEBUG环境中用来增加一些调试使用的module
- 强制注册提供的
- 创建一个空的对象
_reactInstance, 创建JS环境工厂 --std::shared_ptr<JSExecutorFactory> executorFactory, 这个 executorFactory 是后面真正初始化js相关环境的必要组件 - TurboModule 先忽略
- 使用
DispatchGroup中, 异步在JS Thread中执行_initializeBridge, 其实就是初始化_reactInstance, 注意, 会添加到 DispatchGroup 中 - 使用
preapreBridge DispatchGroup中, 异步加载js bundle, 注意, 会添加到 DispatchGroup中 - 异步等待
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];
}
});
}