RN 原理

启动流程

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

整个RN页面的启动流程图如下所示


启动流程图
  1. 创建ReactRootView 过程
public class MainActivity extends ReactActivity {}
public abstract class ReactActivity extends AppCompatActivity
    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
  private final ReactActivityDelegate mDelegate;

  protected ReactActivity() {
    mDelegate = createReactActivityDelegate();
  }
  protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this, getMainComponentName());
  }
}
public class ReactActivityDelegate {
  public ReactActivityDelegate(ReactActivity activity, @Nullable String mainComponentName) {
    mActivity = activity;
    mMainComponentName = mainComponentName;
  }
  protected void onCreate(Bundle savedInstanceState) {
    if (mMainComponentName != null) {
      loadApp(mMainComponentName);
    }
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
  }
  protected void loadApp(String appKey) {
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    getPlainActivity().setContentView(mReactRootView);
  }
}
  1. 创建 ReactInstanceManager
  public ReactInstanceManager getReactInstanceManager() {
    if (mReactInstanceManager == null) {
      ReactMarker.logMarker(ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_START);
      mReactInstanceManager = createReactInstanceManager();
      ReactMarker.logMarker(ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_END);
    }
    return mReactInstanceManager;
  }

  protected ReactInstanceManager createReactInstanceManager() {
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
    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())
      .setJSIModulesPackage(getJSIModulePackage())
      .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()));
    }
    ReactInstanceManager reactInstanceManager = builder.build();
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
    return reactInstanceManager;
  }
  1. 创建 ReactContext
    ReactContext

    我们看到从最初的 startReactApplication 最终会调用到这里
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的创建,我们先来看看这个方法的两个入参:

  • jsExecutorJSCJavaScriptExecutor继承于JavaScriptExecutor,当该类被加载时,它会自动去加载"reactnativejnifb.so"库,并会调用Native方法initHybrid()初始化C++层RN与JSC通信的框架。
  • jsBundleLoader:缓存了JSBundle的信息,封装了上层加载JSBundle的相关接口,CatalystInstanceImpl 实现了 JSBundleLoaderDelegate接口,可以间接调用ReactBridge的 native 方法去加载JS文件,不同的场景会创建不同的加载器,具体可以查看类JSBundleLoader。

可以看到在ReactContext创建的过程中,主要做了以下几件事情:

  1. 构建ReactApplicationContext对象,ReactApplicationContext是ReactContext的包装类。
  2. 利用jsExecutornativeModuleRegistryjsBundleLoaderexceptionHandler等参数构建CatalystInstance实例,作为以为三端通信的中枢。
  3. 调用CatalystInstance.runJSBundle()开始加载执行JS。

另一个重要的角色CatalystInstance出现了,前面我们也说过它是三端通信的中枢。关于通信的具体实现我们会在接下来的通信机制小节来讲述,我们先来接着看JS的加载过程。

加载 JS Bundle

首先是CatalystInstanceImpl

  private CatalystInstanceImpl(
      final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry nativeModuleRegistry,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl");
    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);
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

    Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge");
    //在C++层初始化通信桥
    initializeBridge(
      new BridgeCallback(this),
      jsExecutor,
      mReactQueueConfiguration.getJSQueueThread(),
      mNativeModulesQueueThread,
      mNativeModuleRegistry.getJavaModules(this),
      mNativeModuleRegistry.getCxxModules());
    Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

    mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
  }

ReactQueueConfigurationSpec用来创建ReactQueueConfiguration的实例,ReactQueueConfiguration
同样是个接口,它的实现类是ReactQueueConfigurationImplReactQueueConfiguration的定义如下

public interface ReactQueueConfiguration {
  //UI线程
  MessageQueueThread getUIQueueThread();
  //Native线程
  MessageQueueThread getNativeModulesQueueThread();
  //JS线程
  MessageQueueThread getJSQueueThread();
  void destroy();
}

JS Bundle的加载实际上是指C++层完成的,我们看一下序列图

runJSBundle

JSBundle 有三种加载方式:setSourceURLs, loadScriptFromAssets, loadScriptFromFile
而真正执行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;
        }
      }

    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));
      }
    }

可以看到这个方法主要是调用JavaScriptCore里的evaluateSourceCode()方法验证JS是否有效,并解释执行。然后再调用flush()方法调用JS层里的方法执行JS Bundle。

上面提到flush()方法调用MessageQueue.js的flushedQueue()方法,这是如何做到的呢?
事实上这是由bindBridge()方法来完成的,它从__fbBatchedBridge 对象中取出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();
  });
}

绑定ReactContext与ReactRootView

JS Bundle加载完成以后,前面说的createReactContext()就执行完成了,然后开始执行setupReacContext()方法,绑定ReactContextReactRootView。 我们来看一下它的实现。

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();
        //...
      }
}

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页面的渲染流程序列图,如下所示:


整体渲染过程

大体过程:

  1. React Native将代码由JSX转化为JS组件,启动过程中利用instantiateReactComponentReactElement转化为复合组件ReactCompositeComponent与元组件ReactNativeBaseComponent,利用ReactReconciler对他们进行渲染。
  2. UIManager.js利用C++层的Instance.cpp将UI信息传递给UIManagerModule.java,并利用UIManagerModule.java构建UI。
  3. UIManagerModule.java接收到UI信息后,将UI的操作封装成对应的Action,放在队列中等待执行。各种UI的操作,例如创建、销毁、更新等便在队列里完成,UI最终得以渲染在屏幕上。

JavaScript层组件渲染

JavaScript层组件渲染
  • AppRegistry.registerComponent用来注册组件,在该方法内它会调用AppRegistry.runApplication()来启动js的渲染流程。
  • AppRegistry.runApplication()会将传入的Component转换成ReactElement,并在外面包裹一层AppContaniner,AppContaniner主要用来提供一些debug工具(例如:红盒)
// ReactNativeMount.js
  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()方法。

React组件可以分为两种:

  • 元组件:框架内置的,可以直接使用的组件。例如:View、Image等。它在React Native中用ReactNativeBaseComponent来描述。
  • 复合组件:用户封装的组件,一般可以通过React.createClass()来构建,提供render()方法来返回渲染目标。它在React Native中用ReactCompositeComponent来描述。

instantiateReactComponent(node, shouldHaveDebugID)方法就是根据对象的type生成元组件或者复合组件。

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实例,
  2. 否则,则是用户创建的复合组件,这个时候创建一个ReactCompositeComponentWrapper实例,该实例用来描述复合组件。
  3. 当对象为string或者number时,调用ReactHostComponent.createInstanceForText(node)来注入组件生成逻辑。
  4. 以上都不是,则报错。

我们可以看到,UI渲染主要通过UIManager来完成,UIManager是一个ReactModule,UIManager.js里的操作都会对应到UIManagerModule里来。我们接着来看看Java层的渲染流程

Java层的渲染流程

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注册表,我们需要先理解几个类/接口:

  • 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:是一个接口,实现了该接口则可以被JS层调用,我们在为JS层提供Java API时通常会继承BaseJavaModule/ReactContextBaseJavaModule,这两个类就实现了NativeModule接口。
  • ModuleHolder:NativeModule的一个Holder类,可以实现NativeModule的懒加载。
  • NativeModuleRegistry:Java Module注册表,内部持有Map:Map<Class<? extends NativeModule>, ModuleHolder> mModules,NativeModuleRegistry可以遍历并返回Java Module供调用者使用。

其中 NativeModuleRegistry是在createReactContext()方法里构建的。
JavaScriptModuleRegistry是在CatalystInstanceImpl的构建方法里构建的.
这些都是在CatalystInstanceImpl的构建方法里通过native方法initializeBridge()传入了C++层,如下所示:

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));
}
  • callback:CatalystInstanceImpl的静态内部类ReactCallback,负责接口回调。
  • jsExecutor:JS执行器,将JS的调用传递给C++层。
  • jsQueue.getJSQueueThread():JS线程,通过mReactQueueConfiguration.getJSQueueThread()获得,mReactQueueConfiguration通过ReactQueueConfigurationSpec.createDefault()创建。
  • moduleQueue:Native线程,通过mReactQueueConfiguration.getNativeModulesQueueThread()获得,mReactQueueConfiguration 通过 ReactQueueConfigurationSpec.createDefault()创建。
  • javaModules:java modules,来源于mJavaRegistry.getJavaModules(this)。它对应了C++层JavaModuleWrapper.cpp, JS在Java的时候最终会调用到这个类的inovke()方法上。
  • cxxModules:c++ modules,来源于mJavaRegistry.getCxxModules()。ModuleHolder是NativeModule的一个Holder类,可以实现NativeModule的懒加载。

cxxModules javaModules这两个集合在CatalystInstanceImpl::initializeBridge()被打包成ModuleRegistry传入Instance.cpp 中,并在NativeToJsBridge的构造方法中传给JsToNativeBridge,以后JS如果调用Java就可以通过 ModuleRegistry 来进行调用。

JNI作为C++与Java的桥梁,JSC作为C++与JavaScript的桥梁,而C++最终连接了Java与JavaScript。


RN通信结构
Java 调用 JS
JS 调用 Java
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341