我的测试收藏RN iOS开发相关iOS进阶

(iOS)ReactNative中RCTBridge的建立流程

2015-12-24  本文已影响4501人  Jerry在种草

今年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感兴趣的朋友也一起研究。

上一篇 下一篇

猜你喜欢

热点阅读