React Native 源码浅析

来源:互联网 发布:北京数据堂公司英语 编辑:程序博客网 时间:2024/06/05 03:20

React Native 源码浅析

1.RN是如何完成bundle文件加载的?

2.Native和JS之间是如何通讯的?

3.JS布局是怎么样被渲染到ReactRootView上的?

下面通过对RN源码(版本:0.40.0)的分析,尝试找找这3个问题的答案~

chengyuan-macpro:AwesomeProject chengyuan$ react-native -V0.40.0

一、JSBundle加载过程

上一篇博客React Native 用法介绍有提到过将js文件打包成bundle,存放到assets目录下,RN页面启动后会加载bundle包,下面就具体看下bundle的加载过程~

public abstract class ReactActivity extends Activity    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {  private final ReactActivityDelegate mDelegate;  protected ReactActivity() {    mDelegate = createReactActivityDelegate();  }  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    mDelegate.onCreate(savedInstanceState);  }  //省略非关键代码}

这里使用委托模式将 Activity 的生命周期及事件传递委托给 ReactActivityDelegate进行处理,这样用是为了让 ReactFragmentActivity 也能复用该处理逻辑。

在ReactActivityDelegate的onCreate中调用了loadApp

// ReactActivityDelegate.javaprotected void loadApp(String appKey) {    // 创建RN根视图    mReactRootView = createRootView();    mReactRootView.startReactApplication(      getReactNativeHost().getReactInstanceManager(),      appKey,      getLaunchOptions());     // 将RN视图添加到Activity中    getPlainActivity().setContentView(mReactRootView);}

通过startReactApplication完成ReactContext初始化

// ReactRootView.java  public void startReactApplication(      ReactInstanceManager reactInstanceManager,      String moduleName,      @Nullable Bundle launchOptions) {    // 确保再UI线程运行    UiThreadUtil.assertOnUiThread();    // 省略非关键代码    if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {      mReactInstanceManager.createReactContextInBackground();    }    // 宽高计算完成后添加布局监听    if (mWasMeasured) {      attachToReactInstanceManager();    }  }

createReactContextInBackground这个方法只会在 Application 创建时调用一次,
重新加载 JS时将会调用 recreateReactContextInBackground方法。recreateReactContextInBackground调用了recreateReactContextInBackgroundInner方法

// XReactInstanceManagerImpl.javaprivate void recreateReactContextInBackgroundInner() {    UiThreadUtil.assertOnUiThread();    // 调试模式,从服务器加载 bundle    if (mUseDeveloperSupport && mJSMainModuleName != null) {      // 省略代码      return;    }    // 正式环境,从本地加载 bundle    recreateReactContextInBackgroundFromBundleLoader();  }

继续跟踪,发现启动异步任务ReactContextInitAsyncTask完成ReactContext初始化

// XReactInstanceManagerImpl.java  private final class ReactContextInitAsyncTask extends      AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {    @Override    protected void onPreExecute() {      if (mCurrentReactContext != null) {        tearDownReactContext(mCurrentReactContext);        mCurrentReactContext = null;      }    }    @Override    protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {    // 省略代码      try {        JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();        return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));      } catch (Exception e) {        return Result.of(e);      }    }    // 省略代码}

doInBackground中调用createReactContext创建ReactContext,通过第二个参数params[0].getJsBundleLoader()携带的bundle信息决定从哪里加载bundle文件

 // XReactInstanceManagerImpl.java private ReactApplicationContext createReactContext(      JavaScriptExecutor jsExecutor,      JSBundleLoader jsBundleLoader) {    // 省略代码    catalystInstance.runJSBundle();    return reactContext;  }

runJSBundle执行了mJSBundleLoader.loadScript(CatalystInstanceImpl.this);由于mJSBundleLoader由createAssetLoader创建,所以会调用如下方法

  public static JSBundleLoader createAssetLoader(      final Context context,      final String assetUrl) {    return new JSBundleLoader() {      @Override      public void loadScript(CatalystInstanceImpl instance) {        instance.loadScriptFromAssets(context.getAssets(), assetUrl);      }      @Override      public String getSourceUrl() {        return assetUrl;      }    };  }

instance指向CatalystInstanceImpl,最终调用CatalystInstanceImpl中的native接口:
native void loadScriptFromAssets(AssetManager assetManager, String assetURL);
Bundle 的加载逻辑最终还是交由 C++ 层进行处理。

runJSBundle加载bundle文件流程如下:
这里写图片描述

二、Native与Java通讯机制

Native—>JS

先看下bugly提供的一张完整通讯流程图
这里写图片描述

从createReactContext切入,看下ReactContext上下文对象创建过程中,具体做了哪些操作

// XReactInstanceManagerImpl.javaprivate ReactApplicationContext createReactContext(      JavaScriptExecutor jsExecutor,      JSBundleLoader jsBundleLoader) {    // 省略代码    // 注册JS层模块,通过它把所有的 JavaScriptModule 注册到 CatalystInstance,将 JS 的可调用 API 暴露给 Java。    JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();    final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);    if (mUseDeveloperSupport) {      reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);    }    // 省略代码    try {      CoreModulesPackage coreModulesPackage =        new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);      processPackage(        coreModulesPackage,        reactContext,        moduleSpecs,        reactModuleInfoMap,        jsModulesBuilder);    } finally {      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);    }    // 代码省略}

JavaScriptModuleRegistry.Builder注册了哪些JavaScriptModule呢?需要查看下processPackage接口代码

  private void processPackage(    ReactPackage reactPackage,    ReactApplicationContext reactContext,    List<ModuleSpec> moduleSpecs,    Map<Class, ReactModuleInfo> reactModuleInfoMap,    JavaScriptModuleRegistry.Builder jsModulesBuilder) {    // 省略代码    for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {      jsModulesBuilder.add(jsModuleClass);    }    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);  }

进入到reactPackage.createJSModules的接口中可以查看到添加了哪些JavaScriptModule,代码如下

// CoreModulesPackage.java  @Override  public List<Class<? extends JavaScriptModule>> createJSModules() {    List<Class<? extends JavaScriptModule>> jsModules = new ArrayList<>(Arrays.asList(        DeviceEventManagerModule.RCTDeviceEventEmitter.class,        JSTimersExecution.class,        RCTEventEmitter.class,        RCTNativeAppEventEmitter.class,        AppRegistry.class,        com.facebook.react.bridge.Systrace.class,        HMRClient.class));    if (ReactBuildConfig.DEBUG) {      jsModules.add(DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);      jsModules.add(JSCHeapCapture.HeapCapture.class);      jsModules.add(JSCSamplingProfiler.SamplingProfiler.class);    }    return jsModules;  }

有JSTimersExecution、RCTEventEmitter、RCTNativeAppEventEmitter和HMRClient,这些类继承至JavaScriptModule,以HMRClient类为例,搜索发现还有一个与它对应的HMRClient.js文件,这是为什么?

这里写图片描述

为了找到答案,需要深入了解下接口JavaScriptModule的作用了

package com.facebook.react.bridge;import com.facebook.proguard.annotations.DoNotStrip;/** * Interface denoting that a class is the interface to a module with the same name in JS. Calling * functions on this interface will result in corresponding methods in JS being called. * * When extending JavaScriptModule and registering it with a CatalystInstance, all public methods * are assumed to be implemented on a JS module with the same name as this class. Calling methods * on the object returned from {@link ReactContext#getJSModule} or * {@link CatalystInstance#getJSModule} will result in the methods with those names exported by * that module being called in JS. * * NB: JavaScriptModule does not allow method name overloading because JS does not allow method name * overloading. */@DoNotStrippublic interface JavaScriptModule {}

从 JavaScriptModule 的头注释中,可以了解到大概的意思是:
一个接口如果继承了 JavaScriptModule 接口,做相应的配置,就可以实现这个 Native 接口到 Javascript 中同名模块的映射,调用CatalystInstance#getJSModule接口就可以直接访问js对应的接口~

接下来的重点就是看下getJSModule是如何访问js接口的?

//CatalystInstanceImpl.java  @Override  public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {    return Assertions.assertNotNull(mJSModuleRegistry)        .getJavaScriptModule(this, executorToken, jsInterface);  }

又调用到JavaScriptModuleRegistry的getJavaScriptModule方法,getJavaScriptModule使用了动态代理,所有调用js接口的逻辑都由JavaScriptModuleInvocationHandler#invoke来完成代理,动态代理可以参考之前的博客Design pattern–代理模式

private static class JavaScriptModuleInvocationHandler implements InvocationHandler {    private final WeakReference<ExecutorToken> mExecutorToken;    private final CatalystInstance mCatalystInstance;    private final JavaScriptModuleRegistration mModuleRegistration;    public JavaScriptModuleInvocationHandler(        ExecutorToken executorToken,        CatalystInstance catalystInstance,        JavaScriptModuleRegistration moduleRegistration) {        // 省略代码    }    @Override    public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {      // 省略代码      NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();      mCatalystInstance.callFunction(        executorToken,        mModuleRegistration.getName(),        method.getName(),        jsArgs      );      return null;    }  }

invoke调用了mCatalystInstance.callFunction方法,查看代码

// CatalystInstanceImpl.javaprivate native void callJSFunction(    ExecutorToken token,    String module,    String method,    NativeArray arguments);

有调用到C++接口,进入CatalystInstanceImpl.cpp

// CatalystInstanceImpl.cppvoid CatalystInstanceImpl::callJSFunction(    JExecutorToken* token, std::string module, std::string method, NativeArray* arguments) {  instance_->callJSFunction(token->getExecutorToken(nullptr),                            std::move(module),                            std::move(method),                            std::move(arguments->array));}

instance_是Instance对象,再打开Instance.cpp

// Instance.cppvoid Instance::callJSFunction(ExecutorToken token, std::string&& module, std::string&& method,                              folly::dynamic&& params) {  callback_->incrementPendingJSCalls();  nativeToJsBridge_->callFunction(token, std::move(module), std::move(method), std::move(params));}

接着又调用JSExecutor的callFunction方法,JSExector.cpp 最终将调用转发到 JSCHelper.cpp 中执行evaluateScript 的函数,从而执行 JS 的调用。

至此,从 Java -> C++ 层调用链结束,JSC 将执行 JS 调用,在 JS Framewrok 层接收来自 C++的调用为 MessageQueue.js 的 callFunctionReturnFlushedQueue。在调用 CallFunction 执行 Js 后,会调用 flushedQueue 更新队列。

js—->native

留一张bugly的流程图,源码细节不在赘述:

这里写图片描述

三、RN画面渲染

这里以登录页面的输入框TextInput为例,看下渲染过程~

ReactNative的UI组件渲染流程:

  • js端
    通过requireNativeComponent -> createReactNativeComponentClass -> ReactNativeBaseComponent下mountComponent的调用关系,最终在mountComponent中调用UIManager组件创建View:UIManager.createView(tag, this.viewConfig.uiViewClassName, nativeTopRootTag, updatePayload);

  • Native端
    UIManager调用对应组件类型的ViewManager(单例,管理类)创建实例。

// TextInput.js _renderAndroid: function() {    // 省略代码    const textContainer =      <AndroidTextInput        ref={this._setNativeRef}        {...props}        mostRecentEventCount={0}        onFocus={this._onFocus}        onBlur={this._onBlur}        onChange={this._onChange}        onSelectionChange={this._onSelectionChange}        onTextInput={this._onTextInput}        text={this._getText()}        children={children}        disableFullscreenUI={this.props.disableFullscreenUI}      />;    // 省略代码  },

AndroidTextInput是通过requireNativeComponent引入的, 而requireNativeComponent是require方法引入,可以猜到requireNativeComponent也是一个js文件

// requireNativeComponent.jsfunction requireNativeComponent(  viewName: string,  componentInterface?: ?ComponentInterface,  extraConfig?: ?{nativeOnly?: Object},): Function {  const viewConfig = UIManager[viewName];  if (!viewConfig || !viewConfig.NativeProps) {    warning(false, 'Native component for "%s" does not exist', viewName);    return UnimplementedView;  }  viewConfig.uiViewClassName = viewName;  viewConfig.validAttributes = {};  viewConfig.propTypes = componentInterface && componentInterface.propTypes;  // The ViewConfig doesn't contain any props inherited from the view manager's  // superclass, so we manually merge in the RCTView ones. Other inheritance  // patterns are currenty not supported.  const nativeProps = {    ...UIManager.RCTView.NativeProps,    ...viewConfig.NativeProps,  };  for (const key in nativeProps) {    let useAttribute = false;    const attribute = {};    const differ = TypeToDifferMap[nativeProps[key]];    if (differ) {      attribute.diff = differ;      useAttribute = true;    }    const processor = TypeToProcessorMap[nativeProps[key]];    if (processor) {      attribute.process = processor;      useAttribute = true;    }    viewConfig.validAttributes[key] = useAttribute ? attribute : true;  }  viewConfig.validAttributes.style = ReactNativeStyleAttributes;  if (__DEV__) {    componentInterface && verifyPropTypes(      componentInterface,      viewConfig,      extraConfig && extraConfig.nativeOnly    );  }  return createReactNativeComponentClass(viewConfig);}

viewConfig携带了js所描述关于view的NativeProps,并传递给createReactNativeComponentClass

// createReactNativeComponentClass.jsvar createReactNativeComponentClass = function(  viewConfig: ReactNativeBaseComponentViewConfig): ReactClass<any> {  var Constructor = function(element) {    this._currentElement = element;    this._topLevelWrapper = null;    this._hostParent = null;    this._hostContainerInfo = null;    this._rootNodeID = 0;    this._renderedChildren = null;  };  Constructor.displayName = viewConfig.uiViewClassName;  Constructor.viewConfig = viewConfig;  Constructor.propTypes = viewConfig.propTypes;  Constructor.prototype = new ReactNativeBaseComponent(viewConfig);  Constructor.prototype.constructor = Constructor;  return ((Constructor: any): ReactClass<any>);};

再跟踪到ReactNativeBaseComponent.js,重点看mountComponent做了什么操作

// ReactNativeBaseComponent.jsmountComponent: function(transaction, hostParent, hostContainerInfo, context) {    var tag = ReactNativeTagHandles.allocateTag();    this._rootNodeID = tag;    this._hostParent = hostParent;    this._hostContainerInfo = hostContainerInfo;    // 省略代码    var updatePayload = ReactNativeAttributePayload.create(      this._currentElement.props,      this.viewConfig.validAttributes    );    var nativeTopRootTag = hostContainerInfo._tag;    UIManager.createView(      tag,      this.viewConfig.uiViewClassName,      nativeTopRootTag,      updatePayload    );    ReactNativeComponentTree.precacheNode(this, tag);this._registerListenersUponCreation(this._currentElement.props);    this.initializeChildren(      this._currentElement.props.children,      tag,      transaction,      context    );    return tag;  },};

调用了UIManager.createView,但是跟踪进去的文件是UIManagerStatTracker.js,居然不是UIManager.js,这个就头疼了

进到UIManagerStatTracker.js文件,发现了三个接口:UIManager.createView、UIManager.updateView和UIManager.manageChildren,这三个方法在UIManagerModule.java中也出现过

// UIManagerModule.java@ReactModule(name = "RKUIManager")public class UIManagerModule extends ReactContextBaseJavaModule implements    OnBatchCompleteListener, LifecycleEventListener, PerformanceCounter {@ReactMethod  public void createView(int tag, String className, int rootViewTag, ReadableMap props) {    if (DEBUG) {      FLog.d(          ReactConstants.TAG,          "(UIManager.createView) tag: " + tag + ", class: " + className + ", props: " + props);    }    mUIImplementation.createView(tag, className, rootViewTag, props);  }  @ReactMethod  public void updateView(int tag, String className, ReadableMap props) {    if (DEBUG) {      FLog.d(          ReactConstants.TAG,          "(UIManager.updateView) tag: " + tag + ", class: " + className + ", props: " + props);    }    mUIImplementation.updateView(tag, className, props);  }  /**   * Interface for adding/removing/moving views within a parent view from JS.   *   * @param viewTag the view tag of the parent view   * @param moveFrom a list of indices in the parent view to move views from   * @param moveTo parallel to moveFrom, a list of indices in the parent view to move views to   * @param addChildTags a list of tags of views to add to the parent   * @param addAtIndices parallel to addChildTags, a list of indices to insert those children at   * @param removeFrom a list of indices of views to permanently remove. The memory for the   *        corresponding views and data structures should be reclaimed.   */  @ReactMethod  public void manageChildren(      int viewTag,      @Nullable ReadableArray moveFrom,      @Nullable ReadableArray moveTo,      @Nullable ReadableArray addChildTags,      @Nullable ReadableArray addAtIndices,      @Nullable ReadableArray removeFrom) {    if (DEBUG) {      FLog.d(          ReactConstants.TAG,          "(UIManager.manageChildren) tag: " + viewTag +          ", moveFrom: " + moveFrom +          ", moveTo: " + moveTo +          ", addTags: " + addChildTags +          ", atIndices: " + addAtIndices +          ", removeFrom: " + removeFrom);    }    mUIImplementation.manageChildren(        viewTag,        moveFrom,        moveTo,        addChildTags,        addAtIndices,        removeFrom);  }}

原来Rn和原生类似,也是先渲染内部子控件,然后再渲染外部控件。在render生命周期执行的时候会执行子控件的render方法,子控件会调用UIManager来把信息传递到原始的UIManagerModule,UIManagerModule根据传过来的Tag找到对应的UIManager,最后生成一个Operation添加到UI处理队列中,当mDispatchUIRunnables执行runable的时候调用Operation.execute抽象方法,其实就是调用UIManager.createViewInstance来真正生成View,然后调用viewManager.updateProperties 设置View的属性。这样一个控件就创建出来了。


参考:
https://segmentfault.com/a/1190000004586390
http://doslin.com/2017/03/15/react-native-source-code-analysis/

原创粉丝点击