前言
本文跟随上一篇RN拆包解决方案(一) bundle拆分,已打出common.bundle和patch.bundle文件为前提,引入工程项目中
iOS原生加载流程
RCTBridge开放私有方法
@interface RCTBridge (ReactExecuteScript)
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync;
@end
预先加载common包
在App项目完全启动完成后,可预先加载通用包,缓存到内存中
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"common" withExtension:@"jsbundle"];
bridge = [[RCTBridge alloc] initWithBundleURL:jsCodeLocation
moduleProvider:nil
launchOptions:launchOptions];
加载业务包
当用户跳转路由到某个RN页面时加载patch部分
NSURL *jsCodeLocationBiz = [[NSBundle mainBundle] URLForResource:@"patch" withExtension:@"jsbundle"];
NSError *error = nil;
NSData *sourceBuz = [NSData dataWithContentsOfFile:jsCodeLocationBiz.path
options:NSDataReadingMappedIfSafe
error:&error];
[bridge.batchedBridge executeSourceCode:sourceBuz sync:NO];
需要注意的是,加载业务包的流程必须在common完全加载完毕后执行,否则会出现加载异常;由于没有提供加载完成的回调,只能通过侦听isLoading状态变化来处理
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//加载common
while (bridge.isLoading) {//侦听common加载完成
}
//...
//加载业务包
});
绑定到视图RCTRootView
RCTRootView *view = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:nil];
由此可见,加载common和patch都在同一个RCTBridge容器中执行,最终绑定到RCTRootView展示
Android原生加载流程
使用ReactInstanceManager加载基础包
首先需要初始化RN的运行环境。加载common使公共的模块代码优先执行,不会涉及视图的绑定渲染
jsbundleFile = "assets://common.bundle";
final ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
.setApplication(application)
.addPackage(new MainReactPackage())
.setJSBundleFile(jsbundleFile);
ReactInstanceManager manager = builder.build();
按需加载业务包
通过ReactInstanceManager获取CatalystInstance实例,此实例负责继续加载业务包
ReactContext context = manager.getCurrentReactContext();
CatalystInstance instance = context.getCatalystInstance();
String source = "assets://patch.bundle";
((CatalystInstanceImpl)instance).loadScriptFromAssets(context.getAssets(), source,loadSynchronously);
同理,加载业务包需要在common加载完成后执行
//加载common包
//...
if (!manager.hasStartedCreatingInitialContext()) {
manager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext context) {
manager.removeReactInstanceEventListener(this);
//...
//加载业务包
}
});
manager.createReactContextInBackground();
}
绑定到视图
当用户进入RN页面时在activity层创建并显示
reactRootView = new ReactRootView(this);
reactRootView.startReactApplication(manager, moduleName, null);
setContentView(reactRootView);
总结
RN拆包流程,最终还是要归功于 RN 基于 javascript 设计的灵活性。分步的执行方式能够让我们轻松的将 Bundle 的加载、视图的渲染分步进行,互不影响;