原生RN与58RN启动流程分析

96
LaxusJ
2019.01.22 03:24* 字数 2301

一.原生加载流程

我们知道RN的页面也是依托Activity,React Native框架里有一个ReactActivity,它就是我们RN页面的容器。ReactActivity里有个ReactRootView,正如它的名字那样,它就是
ReactActivity的root View,最终渲染出来的view都会添加到这个ReactRootView上。ReactRootView调用自己的startReactApplication()方法启动了整个RN页面,在启动的过程
中先去创建页面上下文ReactContext,然后再去加载、执行并将JavaScript映射成Native Widget,最终一个RN页面就显示在了用户面前。

1.总流程图

rn启动流程.png

2.核心代码

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启动流程.png

1.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

reactnative入门
Web note ad 1