React Native for Android 原理分析與實踐:實現原理

關於做者javascript

郭孝星,程序員,吉他手,主要從事Android平臺基礎架構方面的工做,歡迎交流技術方面的問題,能夠去個人Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。前端

文章目錄java

  • 一 原理概覽
  • 二 啓動流程
    • 2.1 建立ReactInstanceManager
    • 2.2 建立ReactContext
    • 2.3 加載JS Bundle
    • 2.4 綁定ReactContext與ReactRootView
  • 三 渲染原理
    • 3.1 JavaScript層組件渲染
    • 3.2 Java層組件渲染
  • 四 通訊機制
    • 4.1 建立註冊表
    • 4.2 建立通訊橋

從2016年中開始,我司開始籌措推動React Native在全公司的推廣使用,從最基礎的基礎框架搭建開始,到各類組件庫、開發工具的完善,經歷了諸多波折,也累積了不少經驗。今年的工做也 立刻接近尾聲,打算寫幾篇文章來對這一年多的實踐經驗作個總結。讀者有什麼問題或者想要交流的地方,能夠去vinci提issue。node

預先善其事,必先利其器。開篇第一篇文章咱們仍是從React Native實現原理講起,事實上原理分析的文章以前就有寫過,可是鑑於最新的版本0.52.0源碼有 很多的改動,咱們就再從新溫習一下,這樣過渡到後續的內容,才更加容易理解。react

一 原理概覽

源碼地址:https://github.com/facebook/react-nativec++

源碼版本:git

Build Status
Circle CI
npm version

當你拿到React Native的源碼的時候,它的目錄結構是這樣的:程序員

  • jni:ReactNative的好多機制都是由C、C++實現的,這部分即是用來載入SO庫。
  • perftest:測試配置
  • proguard:混淆
  • quicklog:log輸出
  • react:ReactNative源碼的主要內容,也是咱們分析的主要內容。
  • systrace:system trace
  • yoga:瑜伽?哈哈,並非,是facebook開源的前端佈局引擎

整體來看,整套React Native框架分爲三層,以下圖所示:github

  • Java層:該層主要提供了Android的UI渲染器UIManager(將JavaScript映射成Android Widget)以及一些其餘的功能組件(例如:Fresco、Okhttp)等。
  • C++層:該層主要完成了Java與JavaScript的通訊以及執行JavaScript代碼兩件工做。
  • JavaScript層:該層提供了各類供開發者使用的組件以及一些工具庫。

注:JSCore,即JavaScriptCore,JS解析的核心部分,IOS使用的是內置的JavaScriptCore,Androis上使用的是 https://webkit.org 家的jsc.so。web

經過上面的分析,咱們理解了React Native的框架結構,除此以外,咱們還要理解整套框架裏的一些重要角色,以下所示:

  • ReactContext:ReactContext繼承於ContextWrapper,是ReactNative應用的上下文,經過getContext()去得到,經過它能夠訪問ReactNative核心類的實現。
  • ReactInstanceManager:ReactInstanceManager是ReactNative應用總的管理類,建立ReactContext、CatalystInstance等類,解析ReactPackage生成映射表,而且配合ReactRootView管理View的建立與生命週期等功能。
  • CatalystInstance:CatalystInstance是ReactNative應用Java層、C++層、JS層通訊總管理類,總管Java層、JS層核心Module映射表與回調,三端通訊的入口與橋樑。
  • NativeToJsBridge:NativeToJsBridge是Java調用JS的橋樑,用來調用JS Module,回調Java。
  • JsToNativeBridge:JsToNativeBridge是JS調用Java的橋樑,用來調用Java Module。
  • JavaScriptModule:JavaScriptModule是JS Module,負責JS到Java的映射調用格式聲明,由CatalystInstance統一管理。
  • NativeModule:NativeModule是ava Module,負責Java到Js的映射調用格式聲明,由CatalystInstance統一管理。
  • JavascriptModuleRegistry:JavascriptModuleRegistry是JS Module映射表,NativeModuleRegistry是Java Module映射表

以上即是整套框架中關鍵的角色,值得一提的是,當頁面真正渲染出來之後,它實際上仍是Native代碼,React Native的做用就是把JavaScript代碼映射成Native代碼以及實現兩端 的通訊,因此咱們在React Native基礎框架搭建的過程當中,指導思路之一就是弱化Native與RN的邊界與區別,讓業務開發組感覺不到二者的區別,從而簡化開發流程。

好,有了對React Native框架的總體理解,咱們來繼續分析一個RN頁面是如何啓動並渲染出來的,這也是咱們關心的主要問題。後續的基礎框架的搭建、JS Bundle分包加載、渲染性能優化 等都會圍繞着着一塊作文章。

二 啓動流程

咱們知道RN的頁面也是依託Activity,React Native框架裏有一個ReactActivity,它就是咱們RN頁面的容器。ReactActivity裏有個ReactRootView,正如它的名字那樣,它就是 ReactActivity的root View,最終渲染出來的view都會添加到這個ReactRootView上。ReactRootView調用本身的startReactApplication()方法啓動了整個RN頁面,在啓動的過程 中先去建立頁面上下文ReactContext,而後再去加載、執行並將JavaScript映射成Native Widget,最終一個RN頁面就顯示在了用戶面前。

整個RN頁面的啓動流程圖以下所示:

這個流程看起來有點長,但實際上重要的東西並很少,咱們當前只須要重點關注四個問題:

  1. ReactInstanceManager是如何被建立的,它在建立的時候都初始化了哪些對象?🤔
  2. RN頁面上下文ReactContext在建立的過程當中都作了什麼,都初始化了哪些對象?🤔
  3. JS Bundle是如何被加載的?🤔
  4. JS入口頁面是如何被渲染出來的?🤔

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 建立ReactContext

咱們再來看第二個問題,ReactContext建立流程序列圖以下所示:

能夠發現,最終建立ReactContext是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建立的過程當中,主要作了如下幾件事情:

  1. 構建ReactApplicationContext對象,ReactApplicationContext是ReactContext的包裝類。
  2. 利用jsExecutor、nativeModuleRegistry、jsBundleLoader、exceptionHandler等參數構建CatalystInstance實例,做爲覺得三端通訊的中樞。
  3. 調用CatalystInstance的runJSBundle()開始加載執行JS。

另外一個重要的角色CatalystInstance出現了,前面咱們也說過它是三端通訊的中樞。關於通訊的具體實現咱們會在接下來的通訊機制小節來說述,咱們先來接着看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對象在構建的時候,主要作了兩件事情:

  1. 建立三大線程:UI線程、Native線程與JS線程。
  2. 在C++層初始化通訊橋。

咱們接着來看JS Bundle的加載流程,JS Bundle的加載其實是指C++層完成的,咱們看一下序列圖。

注: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。

void JSCExecutor::flush() {
      SystraceSection s("JSCExecutor::flush");

      if (m_flushedQueueJS) {
          //調用MessageQueue.js的flushedQueue()方法
        callNativeModules(m_flushedQueueJS->callAsFunction({}));
        return;
      }

      // When a native module is called from JS, BatchedBridge.enqueueNativeCall()
      // is invoked. For that to work, require('BatchedBridge') has to be called,
      // and when that happens, __fbBatchedBridge is set as a side effect.
      auto global = Object::getGlobalObject(m_context);
      auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
      // So here, if __fbBatchedBridge doesn't exist, then we know no native calls
      // have happened, and we were able to determine this without forcing
      // BatchedBridge to be loaded as a side effect.
      if (!batchedBridgeValue.isUndefined()) {
        // If calls were made, we bind to the JS bridge methods, and use them to
        // get the pending queue of native calls.
        bindBridge();
        callNativeModules(m_flushedQueueJS->callAsFunction({}));
      } else if (m_delegate) {
        // If we have a delegate, we need to call it; we pass a null list to
        // callNativeModules, since we know there are no native calls, without
        // calling into JS again. If no calls were made and there's no delegate,
        // nothing happens, which is correct.
        callNativeModules(Value::makeNull(m_context));
      }
    }
複製代碼

上面提到flush()方法調用MessageQueue.js的flushedQueue()方法,這是如何作到的呢?🤔。

事實上這是由bindBridge()方法來完成的,bindBridge()從__fbBatchedBridge(__fbBatchedBridge也是被MessageQueue.js設置爲全局變量,供C++層讀取)對象中取出MessageQueue.js裏的四個方法:

  • callFunctionReturnFlushedQueue()
  • invokeCallbackAndReturnFlushedQueue()
  • flushedQueue()
  • callFunctionReturnResultAndFlushedQueue()

並分別存在三個C++變量中:

  • m_callFunctionReturnFlushedQueueJS
  • m_invokeCallbackAndReturnFlushedQueueJS
  • m_flushedQueueJS
  • m_callFunctionReturnResultAndFlushedQueueJS

這樣C++就能夠調用這四個JS方法。

void JSCExecutor::bindBridge() throw(JSException) {
  SystraceSection s("JSCExecutor::bindBridge");
  std::call_once(m_bindFlag, [this] {
    auto global = Object::getGlobalObject(m_context);
    auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined()) {
      auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");
      if (!requireBatchedBridge.isUndefined()) {
        batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
      }
      if (batchedBridgeValue.isUndefined()) {
        throw JSException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
      }
    }

    auto batchedBridge = batchedBridgeValue.asObject();
    m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
    m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
    m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
    m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
  });
}
複製代碼

至此,JS Bundle已經加載解析完成,進入MessageQueue.js開始執行。

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的綁定,綁定的過程主要作兩件事情:

  1. 將ReactRootView做爲根佈局.
  2. 執行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顯示在手機上。

到此爲止,整個RN頁面的啓動流程就分析完了,咱們接着來看看RN頁面是如何渲染最終顯示在手機上的。

三 渲染原理

上面咱們也說了,RN頁面的入口通常是AppRegistry.js,咱們就從這個頁面入手開始分析RN頁面的渲染流程。先看一下RN頁面的渲染流程序列圖,以下所示:

這個流程比較長,其實主要是方法的調用鏈多,原理仍是很簡單的,咱們先歸納性的總結一下:

  1. React Native將代碼由JSX轉化爲JS組件,啓動過程當中利用instantiateReactComponent將ReactElement轉化爲複合組件ReactCompositeComponent與元組件ReactNativeBaseComponent,利用 ReactReconciler對他們進行渲染。
  2. UIManager.js利用C++層的Instance.cpp將UI信息傳遞給UIManagerModule.java,並利用UIManagerModule.java構建UI。
  3. UIManagerModule.java接收到UI信息後,將UI的操做封裝成對應的Action,放在隊列中等待執行。各類UI的操做,例如建立、銷燬、更新等便在隊列裏完成,UI最終 得以渲染在屏幕上。

3.1 JavaScript層組件渲染

如上圖所示AppRegistry.registerComponent用來註冊組件,在該方法內它會調用AppRegistry.runApplication()來啓動js的渲染流程。AppRegistry.runApplication() 會將傳入的Component轉換成ReactElement,並在外面包裹一層AppContaniner,AppContaniner主要用來提供一些debug工具(例如:紅盒)。

以下所示:

function renderApplication<Props: Object>( RootComponent: ReactClass<Props>, initialProps: Props, rootTag: any ) {
  invariant(
    rootTag,
    'Expect to have a valid rootTag, instead got ', rootTag
  );
  ReactNative.render(
    <AppContainer rootTag={rootTag}> <RootComponent {...initialProps} rootTag={rootTag} /> </AppContainer>, rootTag ); } 複製代碼

咱們拋開函數調用鏈,分析其中關鍵的部分,其餘部分都是簡單的函數調用。

  • ReactNativeMount.renderComponent()
  • instantiateReactComponent.instantiateReactComponent(node, shouldHaveDebugID)

ReactNativeMount.renderComponent()

/** * @param {ReactComponent} instance Instance to render. * @param {containerTag} containerView Handle to native view tag */
  renderComponent: function( nextElement: ReactElement<*>, containerTag: number, callback?: ?(() => void) ): ?ReactComponent<any, any, any> {
  
    //將RectElement使用相同的TopLevelWrapper進行包裹
    var nextWrappedElement = React.createElement(
      TopLevelWrapper,
      { child: nextElement }
    );

    var topRootNodeID = containerTag;
    var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
    if (prevComponent) {
      var prevWrappedElement = prevComponent._currentElement;
      var prevElement = prevWrappedElement.props.child;
      if (shouldUpdateReactComponent(prevElement, nextElement)) {
        ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement, emptyObject);
        if (callback) {
          ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
        }
        return prevComponent;
      } else {
        ReactNativeMount.unmountComponentAtNode(containerTag);
      }
    }

    if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
      console.error('You cannot render into anything but a top root');
      return null;
    }

    ReactNativeTagHandles.assertRootTag(containerTag);

    //檢查以前的節點是否已經mount到目標節點上,若是有則進行比較處理
    var instance = instantiateReactComponent(nextWrappedElement, false);
    ReactNativeMount._instancesByContainerID[containerTag] = instance;

    // The initial render is synchronous but any updates that happen during
    // rendering, in componentWillMount or componentDidMount, will be batched
    // according to the current batching strategy.

    //將mount任務提交給回調Queue,最終會調用ReactReconciler.mountComponent()
    ReactUpdates.batchedUpdates(
      batchedMountComponentIntoNode,
      instance,
      containerTag
    );
    var component = instance.getPublicInstance();
    if (callback) {
      callback.call(component);
    }
    return component;
  },
複製代碼

該方法主要作了如下事情:

  1. 將傳入的RectElement使用相同的TopLevelWrapper進行包裹,生成nextWrappedElement。
  2. 檢查以前的節點是否已經mount到目標節點上,若是有則進行比較處理,將上一步生成的nextWrappedElement傳入instantiateReactComponent(nextWrappedElement, false)方法。
  3. 將mount任務提交給回調Queue,最終會調用ReactReconciler.mountComponent(),ReactReconciler.mountComponent()又會去調用C++層Instance::mountComponent() 方法。

instantiateReactComponent.instantiateReactComponent(node, shouldHaveDebugID)

在分析這個函數以前,咱們先來補充一下React組件相關知識。React組件能夠分爲兩種:

  • 元組件:框架內置的,能夠直接使用的組件。例如:View、Image等。它在React Native中用ReactNativeBaseComponent來描述。
  • 複合組件:用戶封裝的組件,通常能夠經過React.createClass()來構建,提供render()方法來返回渲染目標。它在React Native中用ReactCompositeComponent來描述。

instantiateReactComponent(node, shouldHaveDebugID)方法根據對象的type生成元組件或者複合組件。

/** * Given a ReactNode, create an instance that will actually be mounted. * * @param {ReactNode} node * @param {boolean} shouldHaveDebugID * @return {object} A new instance of the element's constructor. * @protected */
function instantiateReactComponent(node, shouldHaveDebugID) {
  var instance;

  if (node === null || node === false) {
    instance = ReactEmptyComponent.create(instantiateReactComponent);
  } else if (typeof node === 'object') {
    var element = node;
    var type = element.type;

    if (typeof type !== 'function' && typeof type !== 'string') {
      var info = '';
      if (process.env.NODE_ENV !== 'production') {
        if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
          info += ' You likely forgot to export your component from the file ' + 'it\'s defined in.';
        }
      }
      info += getDeclarationErrorAddendum(element._owner);
      !false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', type == null ? type : typeof type, info) : _prodInvariant('130', type == null ? type : typeof type, info) : void 0;
    }

    //若是對象的type爲string,則調用ReactHostComponent.createInternalComponent(element)來注入生成組件的邏輯
    if (typeof element.type === 'string') {
      instance = ReactHostComponent.createInternalComponent(element);
    }
    //若是是內部元組件,則建立一個type實例
    else if (isInternalComponentType(element.type)) {
      // This is temporarily available for custom components that are not string
      // representations. I.e. ART. Once those are updated to use the string
      // representation, we can drop this code path.
      instance = new element.type(element);

      // We renamed this. Allow the old name for compat. :(
      if (!instance.getHostNode) {
        instance.getHostNode = instance.getNativeNode;
      }
    } 
    //不然,則是用戶建立的複合組件,這個時候建立一個ReactCompositeComponentWrapper實例,該實例用來描述複合組件
    else {
      instance = new ReactCompositeComponentWrapper(element);
    }
    //當對象爲string或者number時,調用ReactHostComponent.createInstanceForText(node)來注入組件生成邏輯。
  } else if (typeof node === 'string' || typeof node === 'number') {
    instance = ReactHostComponent.createInstanceForText(node);
  } else {
    !false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : _prodInvariant('131', typeof node) : void 0;
  }

  if (process.env.NODE_ENV !== 'production') {
    process.env.NODE_ENV !== 'production' ? warning(typeof instance.mountComponent === 'function' && typeof instance.receiveComponent === 'function' && typeof instance.getHostNode === 'function' && typeof instance.unmountComponent === 'function', 'Only React Components can be mounted.') : void 0;
  }

  // These two fields are used by the DOM and ART diffing algorithms
  // respectively. Instead of using expandos on components, we should be
  // storing the state needed by the diffing algorithms elsewhere.
  instance._mountIndex = 0;
  instance._mountImage = null;

  if (process.env.NODE_ENV !== 'production') {
    instance._debugID = shouldHaveDebugID ? getNextDebugID() : 0;
  }

  // Internal instances should fully constructed at this point, so they should
  // not get any new fields added to them at this point.
  if (process.env.NODE_ENV !== 'production') {
    if (Object.preventExtensions) {
      Object.preventExtensions(instance);
    }
  }

  return instance;
}
複製代碼

該方法根據對象的type生成元組件或者複合組件,具體流程以下:

  1. 若是對象的type爲string,則調用ReactHostComponent.createInternalComponent(element)來注入生成組件的邏輯,若是是內部元組件,則建立一個type實例, 不然,則是用戶建立的複合組件,這個時候建立一個ReactCompositeComponentWrapper實例,該實例用來描述複合組件。
  2. 當對象爲string或者number時,調用ReactHostComponent.createInstanceForText(node)來注入組件生成邏輯。
  3. 以上都不是,則報錯。

咱們經過前面的分析,瞭解了整個UI開始渲染的時機,以及js層的整個渲染流程,接下來,咱們開始分析每一個js的組件時怎麼轉換成Android的組件,最終顯示在屏幕上的。

上面咱們提到元組件與複合組件,事實上覆合組件也是遞歸遍歷其中的元組件,而後進行渲染。因此咱們重點關注元組件的生成邏輯。

咱們能夠看到,UI渲染主要經過UIManager來完成,UIManager是一個ReactModule,UIManager.js裏的操做都會對應到UIManagerModule裏來。咱們接着來看看Java層的 渲染流程。

3.2 Java層組件渲染

從上圖咱們能夠很容易看出,Java層的組件渲染分爲如下幾步:

  1. JS層經過C++層把建立View的請求發送給Java層的UIManagerModule。
  2. UIManagerModule經過UIImplentation對操做請求進行包裝。
  3. 包裝後的操做請求被髮送到View處理隊列UIViewOperationQueue隊列中等待處理。
  4. 實際處理View時,根據class name查詢對應的ViewNManager,而後調用原生View的方法對View進行相應的操做。

四 通訊機制

Java層與JavaScript層的相互調用都不是直接完成的,而是間接經過C++層來完成的。在介紹通訊機制以前咱們先來理解一些基本的概念。

JavaScript Module註冊表

提及JavaScript Module註冊表,咱們須要先理解3個類/接口:JavaScriptModule、JavaScriptModuleRegistration、JavaScriptModuleRegistry。

  • JavaScriptModule:這是一個接口,JS Module都會繼承此接口,它表示在JS層會有一個相同名字的js文件,該js文件實現了該接口定義的方法,JavaScriptModuleRegistry會利用 動態代理將這個接口生成代理類,並經過C++傳遞給JS層,進而調用JS層的方法。
  • JavaScriptModuleRegistration用來描述JavaScriptModule的相關信息,它利用反射獲取接口裏定義的Method。
  • JavaScriptModuleRegistry:JS Module註冊表,內部維護了一個HashMap:HashMap<Class<? extends JavaScriptModule>, JavaScriptModuleRegistration> mModuleRegistrations, JavaScriptModuleRegistry利用動態代理生成接口JavaScriptModule對應的代理類,再經過C++傳遞到JS層,從而調用JS層的方法。

Java Module註冊表

要理解Java Module註冊表,咱們一樣也須要理解3個類/接口:NativeModule、ModuleHolder、NativeModuleRegistry。

  • NativeModule:是一個接口,實現了該接口則能夠被JS層調用,咱們在爲JS層提供Java API時一般會繼承BaseJavaModule/ReactContextBaseJavaModule,這兩個類就 實現了NativeModule接口。
  • ModuleHolder:NativeModule的一個Holder類,能夠實現NativeModule的懶加載。
  • NativeModuleRegistry:Java Module註冊表,內部持有Map:Map<Class<? extends NativeModule>, ModuleHolder> mModules,NativeModuleRegistry能夠遍歷 並返回Java Module供調用者使用。

4.1 建立註冊表

關於NativeModuleRegistry和JavaScriptModuleRegistry的建立,咱們前面都已經提到管,你們還都記得嗎。

  • NativeModuleRegistry是在createReactContext()方法裏構建的。
  • JavaScriptModuleRegistry是在CatalystInstanceImpl的構建方法裏構建的。

這些都是在CatalystInstanceImpl的構建方法裏經過native方法initializeBridge()傳入了C++層,以下所示:

CatalystInstanceImpl.cpp

void CatalystInstanceImpl::initializeBridge(
    jni::alias_ref<ReactCallback::javaobject> callback,
    // This executor is actually a factory holder.
    JavaScriptExecutorHolder* jseh,
    jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
    jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
    jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
    jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {


  instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
                              jseh->getExecutorFactory(),
                              folly::make_unique<JMessageQueueThread>(jsQueue),
                              folly::make_unique<JMessageQueueThread>(moduleQueue),
                              buildModuleRegistry(std::weak_ptr<Instance>(instance_),
                                                  javaModules, cxxModules));
}
複製代碼

這個方法的參數含義以下所示:

  • ReactCallback callback:CatalystInstanceImpl的靜態內部類ReactCallback,負責接口回調。
  • JavaScriptExecutor jsExecutor:JS執行器,將JS的調用傳遞給C++層。
  • MessageQueueThread jsQueue.getJSQueueThread():JS線程,經過mReactQueueConfiguration.getJSQueueThread()得到,mReactQueueConfiguration經過ReactQueueConfigurationSpec.createDefault()建立。
  • MessageQueueThread moduleQueue:Native線程,經過mReactQueueConfiguration.getNativeModulesQueueThread()得到,mReactQueueConfiguration經過ReactQueueConfigurationSpec.createDefault()建立。
  • Collection javaModules:java modules,來源於mJavaRegistry.getJavaModules(this)。
  • Collection cxxModules):c++ modules,來源於mJavaRegistry.getCxxModules()。

咱們注意這些傳入了兩個集合:

  • javaModules:傳入的是Collection ,JavaModuleWrapper是NativeHolder的一個Wrapper類,它對應了C++層JavaModuleWrapper.cpp, JS在Java的時候最終會調用到這個類的inovke()方法上。
  • cxxModules:傳入的是Collection ,ModuleHolder是NativeModule的一個Holder類,能夠實現NativeModule的懶加載。
這兩個集合在CatalystInstanceImpl::initializeBridge()被打包成ModuleRegistry傳入Instance.cpp.、,以下所示:

**ModuleRegistryBuilder.cpp**

```java
std::unique_ptr<ModuleRegistry> buildModuleRegistry(
    std::weak_ptr<Instance> winstance,
    jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
    jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {

  std::vector<std::unique_ptr<NativeModule>> modules;
  for (const auto& jm : *javaModules) {
    modules.emplace_back(folly::make_unique<JavaNativeModule>(winstance, jm));
  }
  for (const auto& cm : *cxxModules) {
    modules.emplace_back(
      folly::make_unique<CxxNativeModule>(winstance, cm->getName(), cm->getProvider()));
  }
  if (modules.empty()) {
    return nullptr;
  } else {
    return folly::make_unique<ModuleRegistry>(std::move(modules));
  }
}
複製代碼

打包好的ModuleRegistry經過Instance::initializeBridge()傳入到NativeToJsBridge.cpp中,並在NativeToJsBridge的構造方法中傳給JsToNativeBridge,之後JS若是調用Java就能夠經過 ModuleRegistry來進行調用。

這裏的NativeToJsBridge.cpp與JsToNativeBridge.cpp就是Java與JS相互調用的通訊橋,咱們來看看它們的通訊方式。

4.2 建立通訊橋

關於整個RN的通訊機制,能夠用一句話來歸納:

JNI做爲C++與Java的橋樑,JSC做爲C++與JavaScript的橋樑,而C++最終鏈接了Java與JavaScript。

RN應用通訊橋結構圖以下所示:

理解了整個通訊橋的結構,Java與JS是如何互相掉用的就很清晰了。

Java調用JS

JS調用Java

注:若是想要了解關於通訊機制的更多細節,能夠去看個人這篇文章6ReactNative源碼篇:通訊機制

相關文章
相關標籤/搜索