20 分钟理解 React Native For Android 原理

BecBrient 8年前
   <ul>     <li>文中所有 RN 缩写指代React Native For Android</li>     <li>分析的 RN 代码基于</li>    </ul>    <pre>  <code class="language-java">{   "react": "15.4.1",   "react-native": "0.39.2"  }  </code></pre>    <ul>     <li>本文主要分析 Java 层实现,对 C++ 和 JS 笔墨较少。</li>     <li>阅读正文将花费您大约20分钟。</li>    </ul>    <h2>背景</h2>    <p>公司内几个 APP 已经接入并上线了多个 RN 模块,后续规划的定制化需求及性能优化需要我们对 RN 底层原理有更深入的理解。下面通过研读源代码来分析和总结下 Android 中的 RN 实现原理。</p>    <h2>从示例入手</h2>    <p>之前写过一篇 弹射起步:Android原生项目集成React Native模块 。</p>    <p>示例代码如下:</p>    <pre>  <code class="language-java">public classMainActivityextendsReactActivity{      @Override      protectedStringgetMainComponentName(){          return "RN_Demo";      }  }  </code></pre>    <p>可以发现 RN 容器外层本质也是一个 <strong>Activity</strong> ,继承了 <strong>ReactActivity</strong> ,需要我们覆写 getMainComponentName() 方法,更改其返回值为组件名。</p>    <h3>ReactActivity</h3>    <p>接着跟踪到 ReactActivity 中,类结构如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ffb5b0b2e8f55d3fe7c084be98d34155.png"></p>    <p>根据上述的结构转换成 UML 图如下(后面相关类将直接给出 UML ):</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5827e6743880afcb143cade0524e18c7.png"></p>    <p>这里使用了 委托模式 将 Activity 的生命周期及事件传递委托给 ReactActivityDelegate 的实例对象 mDelegate 进行处理,之所以使用这种形式是为了让 ReactFragmentActivity 也能复用该处理逻辑。</p>    <p>此外如果你有自定义的委托实现,可以在自己的 Activity 中覆写 createReactActivityDelegate() 方法。这个方法将在 ReactActivity 的构造函数中调用生成 mDelegate 实例,之后在 onCreate() 方法调用这个委托对象的执行入口,也就是 loadApp() 方法。</p>    <h3>ReactActivityDelegate</h3>    <pre>  <code class="language-java">public classReactActivityDelegate{      protectedvoidonCreate(Bundle savedInstanceState){          // 弹窗权限判断逻辑          if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {          // 省略具体实现          }          // 组件加载逻辑          if (mMainComponentName != null) {            loadApp(mMainComponentName);          }          // 双击判断工具类 DoubleTapReloadRecognizer ,省略代码        }          protectedvoidloadApp(String appKey){          // 省略判空代码                    // 创建 RN 容器根视图,父类为 FrameLayout          mReactRootView = createRootView();          mReactRootView.startReactApplication(            getReactNativeHost().getReactInstanceManager(),            appKey,            getLaunchOptions());          // 将 RN 视图放入 Activity          getPlainActivity().setContentView(mReactRootView);        }  }  </code></pre>    <h3>ReactRootView</h3>    <p>因此可以认为所谓的 RN 其实就是一个特殊的“自定义 View ”– ReactRootView 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3fedd8c3f7b0f77705098a506df62313.png"></p>    <p>这里的关键调用则是 startReactApplication() 方法。下面是该方法需要的三个参数:</p>    <table>     <thead>      <tr>       <th>形参</th>       <th>类型</th>       <th>功能描述</th>      </tr>     </thead>     <tbody>      <tr>       <td>reactInstanceManager</td>       <td>ReactInstanceManager</td>       <td>用来创建及管理 CatalyInstance (提供原生与JS互调环境)的实例,同时连接调试功能,其生命周期与 ReactRootView 所在 Activity 保持一致。</td>      </tr>      <tr>       <td>moduleName</td>       <td>String</td>       <td>即实参 appKey ,需要保证JS中的 AppRegistry.registerComponent 参数值与 Acitvity 中的 getMainComponentName 返回值一致。</td>      </tr>      <tr>       <td>launchOptions</td>       <td>Bundle(后续版本可能更改为POJO)</td>       <td>默认为null,如果你需要传 Props 给 JS 的话,请覆写 createReactActivityDelegate() 方法,并覆写 getLaunchOptions() 的返回值即可。</td>      </tr>     </tbody>    </table>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3bd9a1bf757e7fb5793b24f5612c704d.png"></p>    <pre>  <code class="language-java">publicvoidstartReactApplication(        ReactInstanceManager reactInstanceManager,        String moduleName,        @Nullable Bundle launchOptions) {      // 确保在 UI 线程执行            // 判断 ReactContext 是否已初始化,没有就异步在后台线程创建      if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {        mReactInstanceManager.createReactContextInBackground();      }      // 宽高计算完成后添加布局监听    }  </code></pre>    <p>startReactApplication() 将调用其中的 createReactContextInBackground() 方法,我们下面来看看这个通过 构造者模式 创建的实现类– XReactInstanceManagerImpl 。</p>    <h3>ReactInstanceManager</h3>    <pre>  <code class="language-java">/**  * 在后台线程异步初始化,这个方法只会在 Application 创建时调用一次,  * 重新加载 JS 时将会调用 recreateReactContextInBackground 方法  */  @Override  publicvoidcreateReactContextInBackground(){      // 首次执行判断逻辑      mHasStartedCreatingInitialContext = true;      recreateReactContextInBackgroundInner();    }  </code></pre>    <p>可以看到不管是 createReactContextInBackground() 还是 recreateReactContextInBackground() ,都是通过 recreateReactContextInBackgroundInner() 来初始化 ReactContext 的。</p>    <pre>  <code class="language-java">privatevoidrecreateReactContextInBackgroundInner(){   // 确保在UI线程执行         // 调试模式,从服务器加载 bundle      if (mUseDeveloperSupport && mJSMainModuleName != null) {        // 省略代码        return;      }         // 正式环境,从本地加载 bundle      recreateReactContextInBackgroundFromBundleLoader();    }        privatevoidrecreateReactContextInBackgroundFromBundleLoader(){      recreateReactContextInBackground(          new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),          mBundleLoader);    }  }  </code></pre>    <table>     <thead>      <tr>       <th>形参</th>       <th>类型</th>       <th>功能描述</th>      </tr>     </thead>     <tbody>      <tr>       <td>jsExecutorFactory</td>       <td>JavaScriptExecutor.Factory</td>       <td>管理Webkit 的 JavaScriptCore,JS与C++的双向通信在这里中转</td>      </tr>      <tr>       <td>jsBundleLoader</td>       <td>JSBundleLoader</td>       <td>bundle加载器,根据 ReactNativeHost 中的配置决定从哪里加载bundle文件</td>      </tr>     </tbody>    </table>    <h3>XReactInstanceManagerImpl</h3>    <pre>  <code class="language-java">privatevoidrecreateReactContextInBackground(        JavaScriptExecutor.Factory jsExecutorFactory,        JSBundleLoader jsBundleLoader) {         // 省略代码        / 把两个参数封装成ReactContextInitParams对象      ReactContextInitParams initParams =          new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);      if (mReactContextInitAsyncTask == null) {        // 核心代码,创建后台线程,初始化 ReactContext        mReactContextInitAsyncTask = new ReactContextInitAsyncTask();        mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);      } else {        // 省略代码      }    }  </code></pre>    <h3>ReactContextInitAsyncTask</h3>    <pre>  <code class="language-java">privateReactApplicationContextcreateReactContext(        JavaScriptExecutor jsExecutor,        JSBundleLoader jsBundleLoader) {      // 省略代码            // 注册JS层模块,通过它把所有的 JavaScriptModule 注册到 CatalystInstance,将 JS 的可调用 API 暴露给 Java。      JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();        // 包装 ApplicationContext 为 ReactApplicationContext      final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);      // 调试模式      if (mUseDeveloperSupport) {      // 调试模式下 ReactApplicationContext 中崩溃信息由 mDevSupportManager 进行拦截处理(红色背景的错误页)      reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);      }        // 省略代码            try {        // 加载给定的大量核心 ReactPackage,详细列表请查看后文的表格        CoreModulesPackage coreModulesPackage =          new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);        processPackage(          coreModulesPackage,          reactContext,          moduleSpecs,          reactModuleInfoMap,          jsModulesBuilder);      } finally {        // 省略代码      }        // 加载 MainPackage 和开发者自定义的 ReactPackage,逻辑同 CoreModulesPackage      for (ReactPackage reactPackage : mPackages) {        // 省略代码                try {          processPackage(            reactPackage,            reactContext,            moduleSpecs,            reactModuleInfoMap,            jsModulesBuilder);        } finally {          // 省略代码        }      }            // 省略代码            // 注册 Java 层模块,通过它把所有的 NativeModule 注册到 CatalystInstance ,将 Java 的可调用 API 暴露给 JS。      NativeModuleRegistry nativeModuleRegistry;      try {         nativeModuleRegistry = new NativeModuleRegistry(moduleSpecs, reactModuleInfoMap);      } finally {        // 省略代码      }        // 异常处理器选择逻辑      NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null          ? mNativeModuleCallExceptionHandler          : mDevSupportManager;                // 核心逻辑, Builder 模式创建 CatalystInstance 实例      CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()          .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())          // 将 JS 执行通信类传给 CatalystInstance          .setJSExecutor(jsExecutor)          // 将 Java 模块映射表传给 CatalystInstance          .setRegistry(nativeModuleRegistry)          // 将 JS 模块映射表传给 CatalystInstance          .setJSModuleRegistry(jsModulesBuilder.build())          // 将 Bundle 加载工具类传给 CatalystInstance          .setJSBundleLoader(jsBundleLoader)          // 将异常处理器传给 CatalystInstance          .setNativeModuleCallExceptionHandler(exceptionHandler);        // 省略代码           final CatalystInstance catalystInstance;      try {        catalystInstance = catalystInstanceBuilder.build();      } finally {        // 省略代码      }        if (mBridgeIdleDebugListener != null) {        catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);      }         // 使用 CatalystInstance 实例初始化 ReactContext 部分成员变量      reactContext.initializeWithInstance(catalystInstance);            // 解析 Bundle 文件      catalystInstance.runJSBundle();        return reactContext;    }  </code></pre>    <table>     <thead>      <tr>       <th>原生模块</th>       <th>功能描述</th>      </tr>     </thead>     <tbody>      <tr>       <td>AndroidInfoModule</td>       <td>获取Android版本号和本地服务器地址</td>      </tr>      <tr>       <td>AnimationsDebugModule</td>       <td>监听动画过渡性能</td>      </tr>      <tr>       <td>DeviceEventManagerModule</td>       <td>事件监听,比如后退键</td>      </tr>      <tr>       <td>ExceptionsManagerModule</td>       <td>异常处理</td>      </tr>      <tr>       <td>HeadlessJsTaskSupportModule</td>       <td>通知部分JS任务执行完成</td>      </tr>      <tr>       <td>SourceCodeModule</td>       <td>传递Bundle文件地址</td>      </tr>      <tr>       <td>Timing</td>       <td>在绘制帧率变化时触发JS定时器</td>      </tr>      <tr>       <td>UIManagerModule</td>       <td>提供JS去创建和更新原生视图的能力</td>      </tr>      <tr>       <td>DebugComponentOwnershipModule</td>       <td>调试功能:异步请求视图结构</td>      </tr>      <tr>       <td>JSCHeapCapture</td>       <td>调试功能:获取堆内存信息</td>      </tr>      <tr>       <td>JSCSamplingProfiler</td>       <td>调试功能:dump工具</td>      </tr>     </tbody>    </table>    <h3>CatalystInstance</h3>    <pre>  <code class="language-java">privateCatalystInstanceImpl(        final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,        final JavaScriptExecutor jsExecutor,        final NativeModuleRegistry registry,        final JavaScriptModuleRegistry jsModuleRegistry,        final JSBundleLoader jsBundleLoader,        NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {      // 省略代码            // Android UI 线程,JS 线程和 NativeModulesQueue 线程      mReactQueueConfiguration = ReactQueueConfigurationImpl.create(          ReactQueueConfigurationSpec,          new NativeExceptionHandler());                // 调用 C++ 层代码进行初始化      initializeBridge(        new BridgeCallback(this),        jsExecutor,        mReactQueueConfiguration.getJSQueueThread(),        mReactQueueConfiguration.getNativeModulesQueueThread(),        // 将所有 Java Module 传递给 C++ 层        mJavaRegistry.getModuleRegistryHolder(this));      mMainExecutorToken = getMainExecutorToken();    }      @Override    publicvoidrunJSBundle(){      // 省略代码            mJSBundleHasLoaded = true;      // Bundle 的加载逻辑请参看下文的流程图      mJSBundleLoader.loadScript(CatalystInstanceImpl.this);        // 省略代码    }  </code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e66e6c09424eb2022a270c24a147a1b2.png"></p>    <pre>  <code class="language-java">privatenativevoidinitializeBridge(ReactCallback callback,                                         JavaScriptExecutor jsExecutor,                                         MessageQueueThread jsQueue,                                         MessageQueueThread moduleQueue,                                         ModuleRegistryHolder registryHolder);                                           nativevoidloadScriptFromAssets(AssetManager assetManager, String assetURL);  nativevoidloadScriptFromFile(String fileName, String sourceURL);  nativevoidloadScriptFromOptimizedBundle(String path, String sourceURL,intflags);  </code></pre>    <p>此时已经发现Java层的逻辑已经走完,不管是 CatalystInstance 实例的初始化还是 <strong>Bundle</strong> 的加载逻辑都将由 C++ 层进行处理。</p>    <h3>CatalystInstance的创建</h3>    <p>CatalystInstanceImpl.cpp 中是空实现,具体实现在 Instance.cpp 中。</p>    <pre>  <code class="language-java">void CatalystInstanceImpl::initializeBridge(   // JInstanceCallback 实现类,父类在 cxxreact/Instance.h 中。      std::unique_ptr<InstanceCallback> callback,      // 对应 Java 中的 JavaScriptExecutor      std::shared_ptr<JSExecutorFactory> jsef,      // C++ 的 JMessageQueueThread 。      std::shared_ptr<MessageQueueThread> jsQueue,      // C++ 的 JMessageQueueThread 。      std::unique_ptr<MessageQueueThread> nativeQueue,      // C++ 的 ModuleRegistryHolder 的 getModuleRegistry() 方法      std::shared_ptr<ModuleRegistry> moduleRegistry) {    callback_ = std::move(callback);      jsQueue->runOnQueueSync(      [this, &jsef, moduleRegistry, jsQueue,       nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {        // 创建 NativeToJsBridge 对象        nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(            jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);          std::lock_guard<std::mutex> lock(m_syncMutex);        m_syncReady = true;        m_syncCV.notify_all();      });      CHECK(nativeToJsBridge_);  }  </code></pre>    <h3>Bundle 的加载</h3>    <pre>  <code class="language-java">void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,                                                  const std::string& assetURL) {    const int kAssetsLength = 9;  // strlen("assets://");    // 获取 source 路径名,不包含前缀,默认值为 index.android.bundle    auto sourceURL = assetURL.substr(kAssetsLength);    // assetManager 是Java 传递的 AssetManager 。    // extractAssetManager 是JSLoader.cpp 中通过系统动态链接库 android/asset_manager_jni.h的AAssetManager_fromJava 方法来获取 AAssetManager 对象的。    auto manager = react::extractAssetManager(assetManager);    // //通过 JSLoader 对象的 loadScriptFromAssets 方法读文件,得到大字符串 script(即index.android.bundle文件内容)。    auto script = react::loadScriptFromAssets(manager, sourceURL);    // 判断是否为 Unbundle    if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {      instance_->loadUnbundle(        folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),        std::move(script),        sourceURL);      return;    } else {      // instance_ 为 ReactCommon 目录下 Instance.h 中类的实例      instance_->loadScriptFromString(std::move(script), sourceURL);    }  }  </code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ecfb7e0e9971fc226f19eef47032214a.png"></p>    <p>至此 C++ 层的调用逻辑到此为止,感兴趣的同学可以继续跟踪进入,或者可以参考文末的资料,我们下面假设底层执行完成,回到 ReactContextInitAsyncTask 的 onPostExecute 方法。</p>    <pre>  <code class="language-java">@Override  protectedvoidonPostExecute(Result<ReactApplicationContext> result){        // 省略代码                setupReactContext(result.get());                // 省略代码          // 处理队列再次初始化 ReactContext 的逻辑代码  }        privatevoidsetupReactContext(ReactApplicationContext reactContext){      // 省略代码            // 确保在 UI 线程中执行并且当前 ReactContext 为空      Assertions.assertCondition(mCurrentReactContext == null);            // 确保当前 CatalystInstance 实例非空      CatalystInstance catalystInstance =          Assertions.assertNotNull(reactContext.getCatalystInstance());      // 初始化所有 Native Module      catalystInstance.initialize();            mDevSupportManager.onNewReactContextCreated(reactContext);      // 添加内存警告的回调      mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);            moveReactContextToCurrentLifecycleState();        for (ReactRootView rootView : mAttachedRootViews) {        // 核心代码,给 RootView 添加 view        attachMeasuredRootViewToInstance(rootView, catalystInstance);      }      // 省略代码  }      privatevoidattachMeasuredRootViewToInstance(        ReactRootView rootView,        CatalystInstance catalystInstance) {        // 省略代码                // 确保在 UI 线程执行        // 重置视图内容      rootView.removeAllViews();      rootView.setId(View.NO_ID);        // 设置 RootView      UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);      int rootTag = uiManagerModule.addMeasuredRootView(rootView);      rootView.setRootViewTag(rootTag);      @Nullable Bundle launchOptions = rootView.getLaunchOptions();      WritableMap initialProps = Arguments.makeNativeMap(launchOptions);      String jsAppModuleName = rootView.getJSModuleName();        // 获取 AppRegistry 的代理对象      WritableNativeMap appParams = new WritableNativeMap();      appParams.putDouble("rootTag", rootTag);      appParams.putMap("initialProps", initialProps);      // AppRegistry 是 JS 暴露给 Java 层的 API      catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);            // 省略代码  }  </code></pre>    <h3>AppRegistry</h3>    <pre>  <code class="language-java">public interfaceAppRegistryextendsJavaScriptModule{    voidrunApplication(String appKey, WritableMap appParameters);    voidunmountApplicationComponentAtRootTag(introotNodeTag);    voidstartHeadlessTask(inttaskId, String taskKey, WritableMap data);  }  </code></pre>    <p>AppRegistry 的 runApplication() 方法成为了加载 index.android.js 的主入口,而所有的 JS 方法调用都会经过 JavaScriptModuleInvocationHandler 。</p>    <h3>JavaScriptModuleInvocationHandler</h3>    <pre>  <code class="language-java">@Override  public @Nullable Objectinvoke(Object proxy, Method method, @Nullable Object[] args)throwsThrowable{    ExecutorToken executorToken = mExecutorToken.get();            // 省略代码            NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();    // 调用 C++ 层的 callFunction    mCatalystInstance.callFunction(          executorToken,          mModuleRegistration.getName(),          method.getName(),          jsArgs        );        return null;  }  </code></pre>    <p>以上就是 RN Android 的执行主流程,如有疏漏,欢迎留言。</p>    <h2>参考资料</h2>    <ol>     <li><a href="/misc/goto?guid=4958869360543124738" rel="nofollow,noindex">https://github.com/非死book/react-native</a></li>     <li><a href="/misc/goto?guid=4959741993629397549" rel="nofollow,noindex">ReactNative Android源码分析</a></li>     <li><a href="/misc/goto?guid=4959741993726108158" rel="nofollow,noindex">React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)</a></li>     <li><a href="/misc/goto?guid=4959741993816613042" rel="nofollow,noindex">React Native通讯原理</a></li>     <li><a href="/misc/goto?guid=4959741993901929910" rel="nofollow,noindex">ReactNativeAndroid源码分析-Js如何调用Native的代码</a></li>    </ol>    <p> </p>    <p>来自:http://doslin.com/2017/03/15/react-native-source-code-analysis/</p>    <p> </p>