一.原生加载流程
我们知道RN的页面也是依托Activity,React Native框架里有一个ReactActivity,它就是我们RN页面的容器。ReactActivity里有个ReactRootView,正如它的名字那样,它就是
ReactActivity的root View,最终渲染出来的view都会添加到这个ReactRootView上。ReactRootView调用自己的startReactApplication()方法启动了整个RN页面,在启动的过程
中先去创建页面上下文ReactContext,然后再去加载、执行并将JavaScript映射成Native Widget,最终一个RN页面就显示在了用户面前。
1.总流程图
rn启动流程.png2.核心代码
2.1 创建 ReactInstanceManager
我们先来看第一个问题,我们都知道要使用RN页面,就需要先初始化一个ReactNativeHost,它是一个抽象类,ReactInstanceManager就是在这个类里被创建的,如下所示:
public abstract class ReactNativeHost {
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
//应用上下文
.setApplication(mApplication)
//JSMainModuleP相当于应用首页的js Bundle,可以传递url从服务器拉取js Bundle
//当然这个只在dev模式下可以使用
.setJSMainModulePath(getJSMainModuleName())
//是否开启dev模式
.setUseDeveloperSupport(getUseDeveloperSupport())
//红盒的回调
.setRedBoxHandler(getRedBoxHandler())
//JS执行器
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
//自定义UI实现机制,这个我们一般用不到
.setUIImplementationProvider(getUIImplementationProvider())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
//添加我们外面设置的Package
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
//获取js Bundle的加载路径
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
return builder.build();
}
}
2.2 createReactContext
public class ReactInstanceManager {
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
//ReactApplicationContext是ReactContext的包装类。
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
//debug模式里开启异常处理器,就是我们开发中见到的调试工具(红色错误框等)
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
//创建JavaModule注册表
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
//创建CatalystInstanceImpl的Builder,它是三端通信的管理类
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
//JS执行器
.setJSExecutor(jsExecutor)
//Java Module注册表
.setRegistry(nativeModuleRegistry)
//JS Bundle加载器
.setJSBundleLoader(jsBundleLoader)
//Java Exception处理器
.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
final CatalystInstance catalystInstance;
//构建CatalystInstance实例
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
//开启加载执行JS Bundle
catalystInstance.runJSBundle();
//关联catalystInstance与reactContext
reactContext.initializeWithInstance(catalystInstance);
return reactContext;
}
}
在这个方法里完成了RN页面上下文ReactContext的创建,我们先来看看这个方法的两个入参:
JSCJavaScriptExecutor jsExecutor:JSCJavaScriptExecutor继承于JavaScriptExecutor,当该类被加载时,它会自动去加载"reactnativejnifb.so"库,并会调用Native方法initHybrid()初始化C++层RN与JSC通信的框架。
JSBundleLoader jsBundleLoader:缓存了JSBundle的信息,封装了上层加载JSBundle的相关接口,CatalystInstance通过其简介调用ReactBridge去加载JS文件,不同的场景会创建
不同的加载器,具体可以查看类JSBundleLoader。
可以看到在ReactContext创建的过程中,主要做了以下几件事情:
构建ReactApplicationContext对象,ReactApplicationContext是ReactContext的包装类。
利用jsExecutor、nativeModuleRegistry、jsBundleLoader、exceptionHandler等参数构建CatalystInstance实例,作为以为三端通信的中枢。
调用CatalystInstance的runJSBundle()开始加载执行JS。
2.3 加载JS Bundle
在分析JS Bundle的加载流程之前,我们先来看一下上面提到CatalystInstance,它是一个接口,其实现类是CatalystInstanceImpl,我们来看看它的构造方法。
public class CatalystInstanceImpl implements CatalystInstance {
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry nativeModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
mHybridData = initHybrid();
//创建三大线程:UI线程、Native线程与JS线程
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
reactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mNativeModuleRegistry = nativeModuleRegistry;
//创建JS Module注册表实例,这个在以前的代码版本中是在上面的createReactContext()方法中创建的
mJSModuleRegistry = new JavaScriptModuleRegistry();
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();
mTraceListener = new JSProfilerTraceListener(this);
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
//在C++层初始化通信桥
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mNativeModuleRegistry.getJavaModules(this),
mNativeModuleRegistry.getCxxModules());
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
}
}
这个函数的入参大部分我们都已经很熟悉了,我们单独说说这个ReactQueueConfigurationSpec,它用来创建ReactQueueConfiguration的实例,ReactQueueConfiguration
同样是个接口,它的实现类是ReactQueueConfigurationImpl。
ReactQueueConfiguration的定义如下:
public interface ReactQueueConfiguration {
//UI线程
MessageQueueThread getUIQueueThread();
//Native线程
MessageQueueThread getNativeModulesQueueThread();
//JS线程
MessageQueueThread getJSQueueThread();
void destroy();
}
可以看着这个接口的作用就是创建三个带消息队列的线程:
UI线程:Android的UI线程,处理和UI相关的事情。
Native线程:主要是完成通信的工作。
JS线程:主要完成JS的执行和渲染工作。
可以看到CatalystInstance对象在构建的时候,主要做了两件事情:
创建三大线程:UI线程、Native线程与JS线程。
在C++层初始化通信桥。
我们接着来看JS Bundle的加载流程,JS Bundle的加载实际上是指C++层完成的,我们看一下序列图。
image.png
注:JS Bundle有三种加载方式:
setSourceURLs(String deviceURL, String remoteURL) :从远程服务器加载。
loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously):从Assets文件夹加载。
loadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously):从文件路径加载。
从这个序列图上我们可以看出,真正加载执行JS的地方就是JSCExector.cpp的loadApplicationScript()方法。
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
...
switch (jsStatus) {
case JSLoadSourceIsCompiled:
if (!bcSourceCode) {
throw std::runtime_error("Unexpected error opening compiled bundle");
}
//调用JavaScriptCore里的方法验证JS是否有效,并解释执行
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
flush();
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
ReactMarker::logTaggedMarker(ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
return;
case JSLoadSourceErrorVersionMismatch:
throw RecoverableError(explainLoadSourceStatus(jsStatus));
case JSLoadSourceErrorOnRead:
case JSLoadSourceIsNotCompiled:
// Not bytecode, fall through.
break;
}
}
...
可以看到这个方法主要是调用JavaScriptCore里的evaluateSourceCode()方法验证JS是否有效,并解释执行。然后在调用flush()方法层调用JS层的里 方法执行JS Bundle。
2.4 绑定ReactContext与ReactRootView
JS Bundle加载完成以后,前面说的createReactContext()就执行完成了,然后开始执行setupReacContext()方法,绑定ReactContext与ReactRootView。 我们来看一下它的实现。
public class ReactInstanceManager {
private void setupReactContext(final ReactApplicationContext reactContext) {
//...
//执行Java Module的初始化
catalystInstance.initialize();
//通知ReactContext已经被创建爱女
mDevSupportManager.onNewReactContextCreated(reactContext);
//内存状态回调设置
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
//复位生命周期
moveReactContextToCurrentLifecycleState();
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
synchronized (mAttachedRootViews) {
//将所有的ReactRootView与catalystInstance进行绑定
for (ReactRootView rootView : mAttachedRootViews) {
attachRootViewToInstance(rootView, catalystInstance);
}
}
//...
}
private void attachRootViewToInstance(
final ReactRootView rootView,
CatalystInstance catalystInstance) {
//...
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
//将ReactRootView作为根布局
final int rootTag = uiManagerModule.addRootView(rootView);
rootView.setRootViewTag(rootTag);
//执行JS的入口bundle页面
rootView.invokeJSEntryPoint();
//...
}
x
}
setupReactContext()方法主要完成每个ReactRootView与catalystInstance的绑定,绑定的过程主要做两件事情:
将ReactRootView作为根布局.
执行JS的入口bundle页面.
JS的页面入口我们可以设置mJSEntryPoint来自定义入口,如果不设置则是默认的入口AppRegistry。
private void defaultJSEntryPoint() {
//...
try {
//...
String jsAppModuleName = getJSModuleName();
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
这里的调用方式实际上就是原生调用JS的方法,它调用的正是我们很熟悉的AppRegistry.js,AppRegistry.js调用runApplication()开始执行JS页面的渲染,最终转换为 Native UI显示在手机上。
3.核心类
ReactContext
ReactContext继承于ContextWrapper,是ReactNative应用的上下文,通过getContext()去获得,通过它可以访问ReactNative核心类的实现。
ReactInstanceManager
ReactInstanceManager是ReactNative应用总的管理类,创建ReactContext、CatalystInstance等类,解析ReactPackage生成映射表,并且配合ReactRootView管理View的创建与生命周期等功能。
ReactRootView
为启动入口核心类,负责监听及分发事件并重新渲染元素,App启动后,其将作为App的root view。
CatalystInstance
CatalystInstance是ReactNative应用Java层、C++层、JS层通信总管理类,总管Java层、JS层核心Module映射表与回调,三端通信的入口与桥梁。
JavaScriptModule
JavaScriptModule是JS Module,负责JS到Java的映射调用格式声明,由CatalystInstance统一管理。
NativeModule
NativeModule是ava Module,负责Java到Js的映射调用格式声明,由CatalystInstance统一管理。
JavascriptModuleRegistry
JS Module映射表,负责将所有JavaScriptModule注册到CatalystInstance,通过Java动态代理调用到Js。
NativeModuleRegistry
是Java Module映射表,即暴露给Js的API集合。
CoreModulePackage
定义核心框架模块,创建NativeModules&JsModules。
二.58加载流程
1.加载基础流程
58启动流程.png1.1 项目启动
LaunchActivity是整个项目启动的Activity,我们主要来看通往RN初始化的入口doAfterSuccess方法
private void doAfterSuccess() {
...
Initiater initiater = new Initiater();
initiater.setMainComponentName("")
.setExceptionHandlerCreator(new InitRunnable.RNExceptionHandlerCreator(null))
.setStatisticsHandler(new InitRunnable.WubaRNStatistics(null))
.isDebuggable(!WubaSetting.IS_RELEASE_PACKGAGE)
.init(this.mContext).subscribe(this.mRNSubscriber);
...
}
Initiater是WubaRNManager的内部类,最终执行到的是WubaRNManager.getInstance().init(context, TextUtils.isEmpty(this.mMainComponentName) ? "" : this.mMainComponentName, this.mDebuggable),我们紧接着进入到WubaRNManager.init()来一探究竟
private Observable<Boolean> init(final Context context, final String mainComponentName, boolean debuggable) {
BundleFileManager.getInstance().prepare(context);
this.mMainComponentName = mainComponentName;
this.mDebuggable = debuggable;
//Log初始化
WubaRNLogger.init(debuggable);
//AOP切点注册
WubaRNSupportor.get().regist(new PointcutsRegistry() {
public List<PointcutSpec> getPointcuts() {}
});
//释放内置资源并检查更新
return RNReleaseInnerBundleHelper.getInstance().release(context).filter(new Func1<Boolean, Boolean>() {
public Boolean call(Boolean aBoolean) {
return aBoolean;
}
}).map(new Func1<Boolean, Boolean>() {
public Boolean call(Boolean aBoolean) {
String configJson = BundleFileManager.getInstance().readConfigJson();
if (TextUtils.isEmpty(configJson)) {
return null;
} else {
//从配置文件中读取bundle信息
OriginalBundleInfo originalBundleInfo = (OriginalBundleInfo)(new Gson()).fromJson(configJson, OriginalBundleInfo.class);
WubaRNManager.this.mCoreVersion = originalBundleInfo.getCommonVer();
Iterator var4 = originalBundleInfo.getData().iterator();
//循环读取业务bundle信息并存入BUNDLES这个map中
while(var4.hasNext()) {
DataEntity dataEntity = (DataEntity)var4.next();
BundleInfo bundleInfox = BundleFileManager.getInstance().readbuzBundleInfoByBundleID(String.valueOf(dataEntity.getBundleId()));
if (!bundleInfox.isEmpty()) {
WubaRNManager.this.BUNDLES.put(bundleInfox.getBundleID(), bundleInfox);
}
}
File bundleBaseDir = BundleFileManager.getInstance().getBundleBaseDir();
File[] children = bundleBaseDir.listFiles();
File[] var14 = children;
int var7 = children.length;
for(int var8 = 0; var8 < var7; ++var8) {
File child = var14[var8];
if (child.isDirectory()) {
String bundleID = child.getName();
BundleInfo bundleInfo = BundleFileManager.getInstance().readbuzBundleInfoByBundleID(bundleID);
if (!bundleInfo.isEmpty()) {
WubaRNManager.this.BUNDLES.put(bundleInfo.getBundleID(), bundleInfo);
}
}
}
WubaRNManager.this.replaceInnerBundleByFERemote();
// WubaRN初始化
WubaRNFactory.getInstance().init(context, mainComponentName);
return originalBundleInfo != null;
}
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());
}
在WubaRNManager.init方法中首先做了BundleFileManager、Log、AOP的初始化工作,然后在RNReleaseInnerBundleHelper.release方法中执行了释放内置资源并检查更新的操作,在map转换中执行了循环读取业务bundle信息并存入BUNDLES这个map中,在方法的最后执行了WubaRN的初始化。
1.2 RN初始化
在WubaRN初始化之前做了一些准备工作,主要构建ReactInstanceManager是在WubaRN中,我们来看下代码
public WubaRN(Context context, String mainComponentName) {
this.mReactInstanceManager = this.buildReactInstanceManager(TextUtils.isEmpty(mainComponentName) ? "index.android" : mainComponentName, context).build();
BundleFileManager.getInstance().prepare(context);
this.preLoadAsync(context.getApplicationContext()).subscribe();
}
在构造方法中首先通过buildReactInstanceManager获取到ReactInstanceManager实例,然后执行了异步的预加载,我们先来看buildReactInstanceManager都做了什么
public ReactInstanceManagerBuilder buildReactInstanceManager(String mainModuleName, Context context) {
if (this.mExceptionHandler == null) {
this.mExceptionHandler = new WubaRN.ExceptionHandler(this);
}
if (context == null) {
this.mExceptionHandler.handleException(new Exception("the context to assemble ReactInstanceManager is null."));
}
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder().setApplication((Application)context.getApplicationContext()).addPackage(new MainReactPackage()).addPackage(new WubaRCTPackage()).setUseDeveloperSupport(RNDebugSwitcher.getInstance().state()).setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
builder.setNativeModuleCallExceptionHandler(this.mExceptionHandler);
if (!TextUtils.isEmpty(mainModuleName)) {
builder.setJSMainModuleName(mainModuleName);
} else {
builder.setJSMainModuleName("index.android");
}
List<WubaBaseReactPackage> packages = RNPackageContainer.getInstance().getPackageExport(WubaBaseReactPackage.class);
Iterator var5 = packages.iterator();
while(var5.hasNext()) {
WubaBaseReactPackage reactPackage = (WubaBaseReactPackage)var5.next();
builder.addPackage(reactPackage);
}
String coreBundleUri = BundleFileManager.getInstance().prepare(context).getCoreBundleFile().getAbsolutePath();
WubaRNLogger.i("ReactInstance prepareReactRootViewAndLoad load bundle " + coreBundleUri, new Object[0]);
builder.setJSBundleLoader(JSBundleLoader.createFileLoader(coreBundleUri));
return builder;
}
在方法中主要构造了builder对象,然后将wuba基础的reactPackage遍历添加到builder中,最后获取coreBundleUri后设置JSBundleLoader,我们再来看preLoadAsync方法
private Observable<WubaRN> preLoadAsync(final Context applicationContext) {
return Observable.create(new OnSubscribe<WubaRN>() {
public void call(final Subscriber<? super WubaRN> subscriber) {
if (WubaRN.tryLoadRNSo(applicationContext) && !RNDebugSwitcher.getInstance().state()) {
WubaRN.this.mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext reactContext) {
WubaRNLogger.i("ReactContext initialized " + WubaRN.this, new Object[0]);
WubaRN.this.mReactContext = reactContext;
WubaRN.this.mHadPreLoaded = true;
if (WubaRN.this.mPreloadListener != null) {
WubaRN.this.mPreloadListener.preloadFinish(WubaRN.this);
}
subscriber.onNext(WubaRN.this);
subscriber.onCompleted();
}
});
File coreBundleFile = BundleFileManager.getInstance().getCoreBundleFile();
if (coreBundleFile.exists()) {
if (!WubaRN.this.mHadPreLoaded) {
try {
WubaRN.this.mReactInstanceManager.createReactContextInBackground();
} catch (Exception var4) {
subscriber.onNext(WubaRN.this);
subscriber.onCompleted();
CatchUICrashManager.getInstance().sendToBugly(new Exception("WubaRNVector prepareReactRootViewAndLoad exception", var4));
}
} else {
WubaRN.this.refreshReactInstanceManager(coreBundleFile.getAbsolutePath());
}
} else {
WubaRNLogger.e("core.android.bundle not exist", new Object[0]);
}
} else {
WubaRNLogger.e("Try load RN .so fail, just return.", new Object[0]);
WubaRN.this.mReactContext = null;
WubaRN.this.mHadPreLoaded = false;
subscriber.onNext(WubaRN.this);
subscriber.onCompleted();
}
}
}).observeOn(Schedulers.newThread()).subscribeOn(AndroidSchedulers.mainThread());
}
这里主要做了两件事,一个是添加了事件监听,另一个就是执行mReactInstanceManager.createReactContextInBackground()了,看到这个方法我们已经很熟悉了,这里就进入到reactnative的加载流程了,到这里基础流程就结束了。
2.加载业务流程
先来看流程图
58业务启动流程.png
再来看一下详细的时序图
业务时序图.png
2.1 获取WubaRN实例
在onCreateView()方法中执行了initView和initData,initData中初始化了_WubaRNTrigger的实例,在_WubaRNTrigger构造中构造了RNCommonPresenter实例并执行了RNCommonPresenter中的initRN方法,我们看一下initRN方法
public void initRN(Context context, String protocol) {
this.mProtocol = protocol;
this.mContext = context.getApplicationContext();
try {
//解析跳转协议
this.mRNCommonBean = (RNCommonBean)(new Gson()).fromJson(protocol, RNCommonBean.class);
} catch (Exception var4) {
var4.printStackTrace();
}
if (this.mRNCommonBean != null) {
//获取ReactNative实例,这里是wuba声明的ReactNative
this.mReactNative = WubaRNManager.getInstance().getReactNative(context, this.mRNCommonBean.getBundleid());
this.mWubaRNExceptionHandler = WubaRNManager.getInstance().getExceptionHandler();
if (this.mReactNative != null) {
this.mReactNative.getWubaRN().setExceptionCallback(new RNCommonPresenter.RNExceptionCallBack(this));
}
if (this.mWubaRNStatistics == null) {
this.mWubaRNStatistics = (new Builder()).assemble(new APIResponseSSOperation()).assemble(new BuzAPISSOperation()).assemble(new BuzRenderTimeSSOperation()).assemble(new EngineInitSSOperation()).assemble(new RNTotalTimeSSOperation()).build(this.mRNCommonBean.getBundleid());
}
} else {
CrashReport.postCatchedException(new Exception("ProtocolException: protocol is " + protocol));
this.mRNCommonBean = RNCommonBean.getDefaultInstance();
}
}
public ReactNative getReactNative(Context context, String bundleID) {
BundleInfo bundleInfo = (BundleInfo)this.BUNDLES.get(bundleID);
if (bundleInfo != null && !RNDebugSwitcher.getInstance().state()) {
获取wubaRN实例
WubaRN wubaRN = WubaRNFactory.getInstance().getWubaRN(context);
return new ReactNative(bundleInfo, wubaRN);
} else {
return null;
}
}
注意这里的ReactNative类是wubaRNstrategy/cache里用于存储的类,到这里bundle的信息以及wubaRN对象就生成并保存到ReactNative实例中了。
2.2 获取业务bundle
从onViewCreated中执行load方法,其中根据bundle来源进行判断,如果来自远程则执行loadDebug方法,来自本地则执行loadRelease方法,我们主要来看_WubaRNTrigger中的loadRelease方法
public void loadRelease() {
this.mPresenter.loadCache(this.createReactRootView()).subscribe(new Subscriber<Boolean>() {
public void onCompleted() {
WubaRNLogger.i("Load cache completed.", new Object[0]);
}
public void onError(Throwable e) {
WubaRNLogger.e(e);
}
public void onNext(Boolean aBoolean) {
if (aBoolean) {
_WubaRNTrigger.this.mPresenter.doHotUpdate(_WubaRNTrigger.this.createReactRootView());
}
}
});
}
在加载前首先执行了createReactRootView方法创建ReactRootView实例,然后执行RNCommonPresenter的doHotUpdate方法
public void doHotUpdate(final ReactRootView reactRootView) {
if (this.mRNCommonBean != null && reactRootView != null) {
...
RNUpdateService.getInstance().requestSingleBundleUpdate(this.mRNCommonBean.getBundleid(), this.mReactNative == null ? "0" : this.mReactNative.getBundleInfo().getVersion(), WubaRNManager.getInstance().getCoreVersion()).subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<RNUpdateBean>() {
...
public void onNext(RNUpdateBean rnUpdateBean) {
if (RNCommonPresenter.this.mWubaRNStatistics != null) {
RNCommonPresenter.this.mWubaRNStatistics.s("RN_fetch_bundle_end", System.currentTimeMillis());
}
if (rnUpdateBean.is_app_force_update() && !TextUtils.isEmpty(rnUpdateBean.getAppurl())) {
WubaRNLogger.d("SingleUpdate:show app force update dialog");
RNCommonPresenter.this.mVector.appNeedUpdate(rnUpdateBean.getAppurl());
} else {
rnUpdateBean.setBundleId(RNCommonPresenter.this.mRNCommonBean.getBundleid());
RNCommonPresenter.this.mRNUpdateBean = rnUpdateBean;
File bundleFile = WubaRNManager.getInstance().getBundleFileManager(RNCommonPresenter.this.mContext).getBundleFileByID(RNCommonPresenter.this.mRNCommonBean.getBundleid());
if (RNCommonPresenter.this.mReactNative != null && !TextUtils.isEmpty(RNCommonPresenter.this.mReactNative.getBundleInfo().getVersion()) && (rnUpdateBean.getVer() == 0 || RNCommonPresenter.this.mReactNative.getBundleInfo().getVersion().equals(String.valueOf(rnUpdateBean.getVer()))) && bundleFile != null && bundleFile.exists()) {
if (bundleFile.exists()) {
if (!RNCommonPresenter.this.mReactNative.getWubaRN().isHadLoadBuz()) {
RNCommonPresenter.this.showContentAndLoadBundle(reactRootView);
} else {
RNCommonPresenter.this.mVector.completeLoadBundle();
RNCommonPresenter.this.protocolError();
}
} else {
WubaRNLogger.e("bundle hot update failed. " + RNCommonPresenter.this.mReactNative == null ? "mReactNative is null, " : ("bundleid = " + RNCommonPresenter.this.mReactNative.getBundleID() + "," + "server version = " + rnUpdateBean.getVer() + "," + RNCommonPresenter.this.mReactNative.getBundleInfo().getVersion() == null ? " original bundle version is empty, " : (" original bundle version = " + RNCommonPresenter.this.mReactNative.getBundleInfo().getVersion() + "," + bundleFile == null ? "bundleFile not exist." : "")), new Object[0]);
}
} else {
RNCommonPresenter.this.downloadBundleAndUpdate(reactRootView);
}
}
}
});
}
}
首先执行requestSingleBundleUpdate方法请求了服务器获取bundle信息
以公寓大类页bundleId88为例:
正式服务器接口:https://app.58.com/api/base/hotupdate/getresource?=&commver=7&ver=0&bundleid=88&appversion=8.17.0
测试服务器接口:https://apptest.58.com/api/base/hotupdate/getresource?=&commver=7&ver=114&bundleid=88&appversion=8.17.0
正式:https://a.58cdn.com.cn/app58/rnpatch/sand/5de1bf9bf3e67ff8fd0673e057162c78.zip
返回测试下载地址:url:https://apptest.58.com/static/newrnpatch/c6c9a0e40ad0c072c0916e26efc9d9ed.zip
存放的实体:RNUpdateBean
然后判断如果文件存在则执行 RNCommonPresenter.this.showContentAndLoadBundle(reactRootView),否则执行RNCommonPresenter.this.downloadBundleAndUpdate(reactRootView)去下载bundle文件
下载文件存放地址:/data/user/0/com.wuba/files/opt_rn/88/5de1bf9bf3e67ff8fd0673e057162c78.zip
2.3 加载业务bundle
执行RNCommonPresenter的loadRN方法,然后调用 this.mReactNative.getWubaRN().loadBuzBundle(bundleInfo)加载业务bundle,我们来看一下loadBuzBundle方法
public void loadBuzBundle(BundleInfo bundleInfo) {
String loadedVersion = (String)this.mHadLoadedBundle.get(bundleInfo.getBundleID());
if ((loadedVersion == null || !loadedVersion.equals(bundleInfo.getVersion())) && this.mReactContext != null) {
WubaRNLogger.i("Load buz bundle : " + bundleInfo.getBundlePath(), new Object[0]);
CatalystInstance catalystInstance = this.mReactContext.getCatalystInstance();
try {
RefCalatystInstance.wrap(catalystInstance).loadScriptFromFile(bundleInfo.getBundlePath());
this.mHadLoadedBundle.put(bundleInfo.getBundleID(), bundleInfo.getVersion());
this.mIsHadLoadBuz = true;
WubaRNLogger.i("Success load buz bundle : " + bundleInfo.getBundlePath(), new Object[0]);
...
}
}
}
com.wuba.rn.strategy.refs.RefCalatystInstance
public void loadScriptFromFile(String buzBundlePath) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method loadScripFromFileMethod = CatalystInstanceImpl.class.getDeclaredMethod("loadScriptFromFile", String.class, String.class);
loadScripFromFileMethod.setAccessible(true);
loadScripFromFileMethod.invoke(this.mCatalystInstance, buzBundlePath, buzBundlePath);
}
可以看到最终是调用到了RefCalatystInstance这个类的loadScriptFromFile方法,在内部通过反射调用到CatalystInstanceImpl的loadScriptFromFile来执行的加载。到这里业务的加载流程就结束了
核心类
WubaRNManager
通过该类可以获取bundle的一些信息,以及创建、获取ReactNative实例等
WubaRnFactory
主要作用是获取WubaRN实例
WubaRN
ReactNative核心管理类 包括基础bundle、业务bundle加载,ReactInstanceManager构建等
RNCommonFragment
RN载体页
_WubaRNTrigger
RNCommonFragment 操作的触发类
RNCommonPresenter
_WubaRNTrigger的业务实现类
核心方法
initRN 初始化RN
registHolder 注册fragment
loadCache 加载缓存
doHotUpdate 开始热更新
downloadBundleAndUpdate 下载buz.bundle并刷新ReactRootView
showContentAndLoadBundle 将ReactRootView的visibility设置为true
load 获取一个成功预加载的WubaRN,然后开始加载buz.bundle
loadRN 通过ReactRootView启动JS的mainComponent
网友评论