美文网首页我的测试收藏RN iOS开发相关iOS进阶
(iOS)ReactNative中RCTBridge的建立流程

(iOS)ReactNative中RCTBridge的建立流程

作者: Jerry在种草 | 来源:发表于2015-12-24 20:43 被阅读4501次

    今年3月Facebook开源的ReactNative框架吸引了国内外许许多多开发者的关注,国内关注程度最高的莫过于天猫的前端团队,他们甚至做了一些根据自身需求的改进,这阵子的D2前端大会里有谈到一些,感兴趣的同学可自行google一下。

    ReactNative的优缺点知乎讨论地特别多了,我这里就不妄加评论。目前用起这套框架,一些简单的功能可以很快实现,特别是component的做法。但是一涉及到网络图片缓存,手势交互,循环利用tableView,ReactNative恐怕就爱莫能助了,还好之前积累了一些代码,只要稍加改动然后通过RCT_EXPORT_METHOD暴露到JS端就可以使用了。

    只使用RN框架(ReactNative缩写,下同)怎满足得了程序猿的好奇心,用JS写的时候总会想着底层如何实现,今天得空看了一下底层的部分代码,算是一个概览,关于RCTBridge的建立流程的。

    看代码的思路一般都是顺藤摸瓜,不过有时候摸着摸着就回不去了,迷失在瓜田中。所以最好的做法是先通读一遍,不深入细节,根据注释了解每一部分实现什么功能,总之就是要有大概的印象,后面要深究时再一部分一部分进行。注意本文谈到的RN框架是0.16版本的。

    1.RCTBridge简介

    可参考bang的通信机制,看完那一篇之后会对整个bridge有大概的了解。

    2.RCTBridge建立流程

    2.1初始化

    在AppDelegate.m中RCTBridge的初始化方法是

    _bridge= [[RCTBridgealloc]initWithDelegate:self

    launchOptions:launchOptions];

    这里设置代理Appdelegate类为RctBridge的代理,会执行什么方法呢,后文会谈到。

    进入此函数后,可以看到下面这个函数。

    - (instancetype)initWithDelegate:(id)delegate

    launchOptions:(NSDictionary*)launchOptions

    {

    RCTAssertMainThread();

    if((self= [superinit])) {

    RCTPerformanceLoggerStart(RCTPLTTI);

    _delegate= delegate;

    _launchOptions= [launchOptionscopy];

    [selfsetUp];

    [selfbindKeys];

    }

    returnself;

    }

    首先是进行主线程的一个检测,确保当前在主线程中。然后是开启性能记录器,传值,接着就是建立bridge和绑定键盘操作了。

    2.2建立过程

    在setup方法里,出现了一个RCTConver,估计是facebook自己写的转换器,用于生成一个_bundleURL,然后就是初始化一个RCTBatchedBridge,注意这里的self.batchedBridge是RCTBridge实例而不是RCTBatchedBridge实例,在这里的是多态的用法。

    - (void)setUp

    {

    RCTAssertMainThread();

    _bundleURL= [self.delegatesourceURLForBridge:self] ?:_bundleURL;

    // Sanitize the

    bundle URL

    _bundleURL= [RCTConvertNSURL:_bundleURL.absoluteString];

    self.batchedBridge= [[RCTBatchedBridgealloc]initWithParentBridge:self];

    }

    初始化过后

    - (void)start

    {

    dispatch_queue_tbridgeQueue =dispatch_queue_create("com.facebook.react.RCTBridgeQueue",DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_tinitModulesAndLoadSource =dispatch_group_create();

    //

    Asynchronously load source code

    dispatch_group_enter(initModulesAndLoadSource);

    __weakRCTBatchedBridge*weakSelf =self;

    __blockNSData*sourceCode;

    [selfloadSource:^(NSError*error,NSData*source) {

    if(error) {

    dispatch_async(dispatch_get_main_queue(), ^{

    [weakSelfstopLoadingWithError:error];

    });

    }

    sourceCode = source;

    dispatch_group_leave(initModulesAndLoadSource);

    }];

    //

    Synchronously initialize all native modules that cannot be loaded lazily

    [selfinitModules];

    #if

    RCT_DEBUG

    _syncInitializedModules = [[_moduleDataByID

    valueForKeyPath:@"@sum.hasInstance"] integerValue];

    #endif

    if(RCTProfileIsProfiling()) {

    //

    Depends on moduleDataByID being loaded

    RCTProfileHookModules(self);

    }

    __blockNSString*config;

    dispatch_group_enter(initModulesAndLoadSource);

    dispatch_async(bridgeQueue, ^{

    dispatch_group_tsetupJSExecutorAndModuleConfig =dispatch_group_create();

    //

    Asynchronously initialize the JS executor

    dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{

    [weakSelfsetUpExecutor];

    });

    //

    Asynchronously gather the module config

    dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{

    if(weakSelf.isValid) {

    RCTPerformanceLoggerStart(RCTPLNativeModulePrepareConfig);

    config = [weakSelfmoduleConfig];

    RCTPerformanceLoggerEnd(RCTPLNativeModulePrepareConfig);

    #if

    RCT_DEBUG

    NSIntegertotal =

    [[_moduleDataByID valueForKeyPath:@"@sum.hasInstance"] integerValue];

    _asyncInitializedModules= total -_syncInitializedModules;

    #endif

    }

    });

    dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{

    //

    We're not waiting for this to complete to leave dispatch group, since

    //

    injectJSONConfiguration and executeSourceCode will schedule operations

    //

    on the same queue anyway.

    RCTPerformanceLoggerStart(RCTPLNativeModuleInjectConfig);

    [weakSelfinjectJSONConfiguration:configonComplete:^(NSError*error) {

    RCTPerformanceLoggerEnd(RCTPLNativeModuleInjectConfig);

    if(error) {

    dispatch_async(dispatch_get_main_queue(),

    ^{

    [weakSelfstopLoadingWithError:error];

    });

    }

    }];

    dispatch_group_leave(initModulesAndLoadSource);

    });

    });

    dispatch_group_notify(initModulesAndLoadSource,dispatch_get_main_queue(), ^{

    RCTBatchedBridge*strongSelf = weakSelf;

    if(sourceCode && strongSelf.loading) {

    dispatch_async(bridgeQueue, ^{

    [weakSelfexecuteSourceCode:sourceCode];

    });

    }

    });

    }

    这个方法挺长,看起来会比较懵。不过一点点看下来还是能看懂的。首先是声明一个bridgeQueue,这个在后面会用到。然后就是声明initModulesAndLoadSource组,看到dispatch_group_t时可以知道这里会做一些线程相关的操作了,特别是多任务并发全部完成后再执行其他任务。这几个task的执行顺序还挺有意思的。

    首先

    异步加载源码、建立JS执行器和模块配置信息是并发的

    只有完成了这两步之后才会异步执行源码。

    初始化所有未延迟的本地模块是在主线程完成的。

    而建立JS执行器和模块配置信息又分三步,异步初始化JS执行器,异步收集模块配置信息,这两步做完后才进行第三部注入JSON配置信息。

    这么一个流程下来后就建立了RCTBridge

    2.3键盘操作绑定

    键盘操作主要是在模拟器上调试可以直接用快捷键Command+ r来进行热加载刷新界面。

    RCTKeyCommands*commands = [RCTKeyCommandssharedInstance];

    // reload in

    current mode

    [commandsregisterKeyCommandWithInput:@"r"

    modifierFlags:UIKeyModifierCommand

    action:^(__unusedUIKeyCommand*command)

    {

    [[NSNotificationCenterdefaultCenter]postNotificationName:RCTReloadNotification

    object:nil

    userInfo:nil];

    }];

    更新到Xcode7之后有一些朋友没法使用Command+r来刷新估计就是这里执行时没反应。

    整个流程就是以上所述,希望对ReactNative感兴趣的朋友也一起研究。

    相关文章

      网友评论

      • krmao:boss, 能否具体分享下, ios react native load business bundle 的代码?

      本文标题:(iOS)ReactNative中RCTBridge的建立流程

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