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

作者: LaxusJ | 来源:发表于2019-01-22 03:24 被阅读16次

    一.原生加载流程

    我们知道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

    相关文章

      网友评论

        本文标题:原生RN与58RN启动流程分析

        本文链接:https://www.haomeiwen.com/subject/xorfjqtx.html