(iOS)ReactNative中RCTBridge的建立流程

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 本篇博客共分以下几个模块来介绍GCD的相关内容: 多线程相关概念 多线程编程技术的优缺点比较? GCD中的三种队列...
    dullgrass阅读 37,826评论 30 236
  • 1、主线程队列 VS 分线程队列 dispatch_sync 和 dispatch_async 区别: dispa...
    瑞小萌阅读 913评论 4 7
  • iOS中GCD的使用小结 作者dullgrass 2015.11.20 09:41*字数 4996阅读 20199...
    DanDanC阅读 810评论 0 0
  • 想你入骨,不想抽。 前面的女孩头发好柔顺你的也这样吗 那个女孩发夹好美和你一样吗 那个女孩的衣服有点清新如果你穿呢...
    我想读书了阅读 232评论 0 0
  • 毕业了,稀里糊涂四处闯荡 家人在这头男友在那头难以取舍 只身一人拖鞋大箱子去了一个中间城市闯荡 自身性格自立还好在...
    我是你大爷你大爷阅读 189评论 0 0