2017-07-05 21:31:18 jett2357 阅读数 340
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57472 人正在学习 去看看 李宁

React Native 源码浅析

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

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

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

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

chengyuan-macpro:AwesomeProject chengyuan$ react-native -V
0.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.java
protected 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.java
private 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.java
private 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.
 */
@DoNotStrip
public 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.java
private native void callJSFunction(
    ExecutorToken token,
    String module,
    String method,
    NativeArray arguments);

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

// CatalystInstanceImpl.cpp
void 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.cpp
void 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.js
function 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.js
var 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.js
mountComponent: 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/

2017-06-27 17:34:44 zhuziyue1202 阅读数 273
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57472 人正在学习 去看看 李宁

1. 下载源码

官方地址:https://github.com/facebook/react-native, 使用clone命令下载文件到本地:

git clone https://github.com/facebook/react-native.git

react native源码包

2. 配置Android Studio

  1. Android SDK version 23 (编译SDK版本号在App的build.gradle中可以找到)
  2. SDK build tools version 23.0.1(编译工具版本号在App的build.gradle中可以找到)
  3. Android Support Repository >= 17
  4. Android NDK

2.1 NDK下载地址

说明:NDK的版本目前必须为 android-ndk-r10e 版本,否则会显示编译错误。

2.2 配置环境变量

设置ANDROIDSDKANDORID_NDK为对应的目录,例如:

export ANDROID_SDK="/Users/*****/Library/Android/sdk"
export ANDROID_NDK="/Users/*****/Library/Android/android-ndk-r10e"
export PATH=${PATH}:${ANDROID_SDK}:${ANDROID_SDK}/platform-tools:${ANDROID_SDK}/tools:${ANDROID_NDK}

2.3 配置local.properties文件

在react_native的根目录下创建local.properties文件,并配置Android SDK和Android NDK的路径,例如:

这里写图片描述

注:以上配置是根据我自己的机器配置的,需自定修改为各位的SDK和NDK的绝对路径,Windows下最好使用反斜杠“\”.

3. 添加Node依赖模块

切换到react_native根目录下,并添加Node_Modules模块,如下:

cd react-native & npm install

这里写图片描述

4. 运行源码

./gradlew :Examples:UIExplorer:android:app:installDebug
// Start the packager in a separate shell (make sure you ran npm install):
./packager/packager.sh
// Open the Movies app in your emulator

这里写图片描述

4.1 运行其他源码

运行Movies文件夹下的DEMO:

./gradlew :Examples:Movies:android:app:installDebug

5. 可能遇到的问题

5.1 boost_1_57_0.tar下载失败

文本提示:Could not expand ZIP ‘/Users/wangchenlong/Exercises/ReactNative/react-native/ReactAndroid/build/downloads/boost_1_57_0.zip’

(1)自行下载(http://www.boost.org/users/history/version_1_57_0.html
(2)删除文件,重新运行./gradlew :Examples:UIExplorer:android:app:installDebug命令

注:Boost 是 Cpp 重要的库, 比较大100M左右, 下载较慢.

5.2 NDK版本不正确,编译错误

这里写图片描述

我之前的版本是R13,修改为 r10e 版本就可以了!

2016-05-30 08:55:07 u014486880 阅读数 17684
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57472 人正在学习 去看看 李宁

学习React-Native有一段时间了。今天就从源码的角度来分析下React-Native底层的通信机制。了解下底层是如何通信的对开发也有所好处。

概要

先大概讲一下React-Native的通信过程。RN主要的通信在于java与js之间,平常我们写的jsx代码最终会调用到原生的View。上一篇博客我们也了解到了要新建一个原生模块需要在java层和js层分别写一个Module,那这彼此之间联系是如何实现的呢?

层次结构

RN总共分为三层,java层,C++层,js层。借用一幅图来看下:
这里写图片描述
Java层:java层就是app原生代码,它通过启动C++层的javascript解析器javascriptCore来执行js代码,从而构建原生UI等。java层依赖于众多优秀开源库,在图片处理使用的是Fresco,网络通信使用的是okhttp,Json解析工具用jackson,动画库用NineOldAndroids等,在java层原生的功能均封装为Module,如Toast和Log等。
C++层:c++层最主要是封装了JavaScriptCore,它是一个全新的支持ES6的webKit。Bridge连接了java与js之间的通信。解析js文件是通过JSCExectutor进行的。
Js层:主要处理事件分发及UI Layout,平常开发最常用的。通用jsx来写业务代码,通过flexbox来实现布局。不依赖DOM。由于react有 DOM diff这个算法,所以它的效率非常高。

通信机制

在Java层与Js层的bridge分别存有相同一份模块配置表,Java与Js互相通信时,通过将里配置表中的moduleID,methodID转换成json数据的形式传递给到C++层,C++层传送到js层,通过js层的的模块配置表找到对应的方法进行执行,如果有callback,则回传给java层。这里只是大概介绍,后面会有详细讲解。

主要流程与主要类

先看下java层的流程图:
这里写图片描述

  • ReactInstanceManager:主要是用来创建及管理CatalyInstance的实例的上层接口,控制开发调试,生命周期与ReactRootView所在activity保持一致。
  • ReactRootView:为启动入口核心类,负责监听及分发事件并重新渲染元素,App启动后,其将作为App的root view。
  • CatalystInstance:提供Java与Js互通的环境,创建Java模块注册表及Javascript模块注册表,并遍历实例化模块,最后通过ReactBridge将Js Bundle传送到Js引擎。
  • JSBuilderLoader:缓存了JsBundle的信息,封装了上层加载JsBundle相关接口,CatalystInstance通过其间接调用ReactBridge去加载文件。
  • NativeModuleRegistry:Java层模块注册表,即暴露给Js的API集合。
  • JavascriptModuleRegistry:Js层模块注册表,负责将所有JavaScriptModule注册到CatalystInstance。
  • CoreModulePackage:CoreModulesPackage里面定义了RN框架核心的一些Java和JS的module,创建NativeModules&JsModules组件模块。

源码分析

加载Module

首先看MainActivity的

  protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
        new  AppReactPackage()
        );
    }

AppReactPackage是我们自定义的一个ReactPackage,也就是说如果自己定义了新组件,要在这里添加。看下ReactActivity,看它的onCreate方法:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(this.getUseDeveloperSupport() && VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(this)) {
            Intent mReactRootView = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION");
            this.startActivity(mReactRootView);
            FLog.w("React", "Overlay permissions needs to be granted in order for react native apps to run in dev mode");
            Toast.makeText(this, "Overlay permissions needs to be granted in order for react native apps to run in dev mode", 1).show();
        }

        this.mReactInstanceManager = this.createReactInstanceManager();
        ReactRootView mReactRootView1 = this.createRootView();
        mReactRootView1.startReactApplication(this.mReactInstanceManager, this.getMainComponentName(), this.getLaunchOptions());
        this.setContentView(mReactRootView1);
    }

主要完成三个功能,通过createReactInstanceManager创建ReactInstanceManager,它主要是用来创建及管理CatalyInstance的实例的上层接口。第二步是通过createRootView来创建ReactRootView。最后调用ReactRootView的startReactApplication来启动应用,并把它当作rootview setContentView到界面上。重点看startReactApplication方法:

   public void startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions) {
        UiThreadUtil.assertOnUiThread();
        Assertions.assertCondition(this.mReactInstanceManager == null, "This root view has already been attached to a catalyst instance manager");
        this.mReactInstanceManager = reactInstanceManager;
        this.mJSModuleName = moduleName;
        this.mLaunchOptions = launchOptions;
        if(!this.mReactInstanceManager.hasStartedCreatingInitialContext()) {
            this.mReactInstanceManager.createReactContextInBackground();
        }

        if(this.mWasMeasured && this.mIsAttachedToWindow) {
            this.mReactInstanceManager.attachMeasuredRootView(this);
            this.mIsAttachedToInstance = true;
            this.getViewTreeObserver().addOnGlobalLayoutListener(this.getKeyboardListener());
        } else {
            this.mAttachScheduled = true;
        }

    }

mJSModuleName是与前端约定好所要启动的JS Application Name。先看createReactContextInBackground方法,它位于ReactInstanceManager的实现类ReactInstanceManagerImpl中:

public void recreateReactContextInBackground() {
    Assertions.assertCondition(this .mHasStartedCreatingInitialContext , "recreateReactContextInBackground should only be called after the initial createReactContextInBackground call.") ;
    this. recreateReactContextInBackgroundInner() ;
}

createReactContextInBackground最终调用到recreateReactContextInBackgroundInner:

private void recreateReactContextInBackgroundInner() {
    UiThreadUtil.assertOnUiThread();
    if( this.mUseDeveloperSupport && this.mJSMainModuleName != null) {
        if (this.mDevSupportManager.hasUpToDateJSBundleInCache()) {
            this .onJSBundleLoadedFromServer() ;
        } else if(this .mJSBundleFile == null) {
            this .mDevSupportManager.handleReloadJS() ;
        } else {
            this .mDevSupportManager.isPackagerRunning( new PackagerStatusCallback() {
                public void onPackagerStatusFetched( final boolean packagerIsRunning) {
                    UiThreadUtil.runOnUiThread( new Runnable() {
                        public void run() {
                            if(packagerIsRunning) {
                                ReactInstanceManagerImpl.this .mDevSupportManager.handleReloadJS() ;
                            } else {
                                ReactInstanceManagerImpl.this .recreateReactContextInBackgroundFromBundleFile() ;
                            }

                        }
                    }) ;
                }
            });
        }

    } else {
        this .recreateReactContextInBackgroundFromBundleFile() ;
    }
}

接着调用recreateReactContextInBackgroundFromBundleFile:

   private void recreateReactContextInBackgroundFromBundleFile() {
        this.recreateReactContextInBackground(new com.facebook.react.bridge.JSCJavaScriptExecutor.Factory(), JSBundleLoader.createFileLoader(this.mApplicationContext, this.mJSBundleFile));
    }

经过一系列的周转,最后调用到了recreateReactContextInBackground:

private void recreateReactContextInBackground(com.facebook.react.bridge.JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) {
    UiThreadUtil.assertOnUiThread();
    ReactInstanceManagerImpl.ReactContextInitParams initParams = new ReactInstanceManagerImpl.ReactContextInitParams(jsExecutorFactory , jsBundleLoader);
    if( this.mReactContextInitAsyncTask == null) {
        this .mReactContextInitAsyncTask = new ReactInstanceManagerImpl.ReactContextInitAsyncTask( null);
        this.mReactContextInitAsyncTask.execute( new ReactInstanceManagerImpl.ReactContextInitParams[]{initParams}) ;
    } else {
        this .mPendingReactContextInitParams = initParams ;
    }

}

上面代码通过ReactContextInitAsyncTask这个AsyncTask来初始化ReactCotext。

 private final class ReactContextInitAsyncTask extends AsyncTask<ReactInstanceManagerImpl.ReactContextInitParams, Void, ReactInstanceManagerImpl.Result<ReactApplicationContext>> {
        private ReactContextInitAsyncTask() {
        }

        protected void onPreExecute() {
            if(ReactInstanceManagerImpl.this.mCurrentReactContext != null) {
                ReactInstanceManagerImpl.this.tearDownReactContext(ReactInstanceManagerImpl.this.mCurrentReactContext);
                ReactInstanceManagerImpl.this.mCurrentReactContext = null;
            }

        }

        protected ReactInstanceManagerImpl.Result<ReactApplicationContext> doInBackground(ReactInstanceManagerImpl.ReactContextInitParams... params) {
            Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);

            try {
                JavaScriptExecutor e = params[0].getJsExecutorFactory().create(ReactInstanceManagerImpl.this.mJSCConfig == null?new WritableNativeMap():ReactInstanceManagerImpl.this.mJSCConfig.getConfigMap());
                return ReactInstanceManagerImpl.Result.of((Object)ReactInstanceManagerImpl.this.createReactContext(e, params[0].getJsBundleLoader()));
            } catch (Exception var3) {
                return ReactInstanceManagerImpl.Result.of(var3);
            }
        }

        protected void onPostExecute(ReactInstanceManagerImpl.Result<ReactApplicationContext> result) {
            try {
                ReactInstanceManagerImpl.this.setupReactContext((ReactApplicationContext)result.get());
            } catch (Exception var6) {
                ReactInstanceManagerImpl.this.mDevSupportManager.handleException(var6);
            } finally {
                ReactInstanceManagerImpl.this.mReactContextInitAsyncTask = null;
            }

            if(ReactInstanceManagerImpl.this.mPendingReactContextInitParams != null) {
                ReactInstanceManagerImpl.this.recreateReactContextInBackground(ReactInstanceManagerImpl.this.mPendingReactContextInitParams.getJsExecutorFactory(), ReactInstanceManagerImpl.this.mPendingReactContextInitParams.getJsBundleLoader());
                ReactInstanceManagerImpl.this.mPendingReactContextInitParams = null;
            }

        }

ReactContextInitAsyncTask为创建ReactContext的核心类,随后,调用createReactContext进一步创建ReactContext。在创建完React Context后会调用setUpReactContext,将ReactRootView做为Root View传递给UIManagerModule,调用AppRegistry的runApplication去启动Js Application等。看createReactContext的代码:

private ReactApplicationContext createReactContext(JavaScriptExecutor jsExecutor , JSBundleLoader jsBundleLoader) {
    FLog.i("React" , "Creating react context.");
    ReactMarker.logMarker( "CREATE_REACT_CONTEXT_START" );
    this.mSourceUrl = jsBundleLoader.getSourceUrl() ;
    Builder nativeRegistryBuilder = new Builder();
    com.facebook.react.bridge.JavaScriptModulesConfig.Builder jsModulesBuilder = new com.facebook.react.bridge.JavaScriptModulesConfig.Builder() ;
    ReactApplicationContext reactContext = new ReactApplicationContext( this.mApplicationContext);
    if( this.mUseDeveloperSupport) {
        reactContext.setNativeModuleCallExceptionHandler(this.mDevSupportManager) ;
    }

    ReactMarker.logMarker("PROCESS_PACKAGES_START" );
    Systrace.beginSection( 0L, "createAndProcessCoreModulesPackage" );

    try {
        CoreModulesPackage nativeModuleRegistry = new CoreModulesPackage( this, this.mBackBtnHandler , this.mUIImplementationProvider);
        this.processPackage(nativeModuleRegistry , reactContext, nativeRegistryBuilder, jsModulesBuilder) ;
    } finally {
        Systrace.endSection(0L );
    }

    Iterator nativeModuleRegistry2 = this .mPackages.iterator();

    while(nativeModuleRegistry2.hasNext()) {
        ReactPackage javaScriptModulesConfig = (ReactPackage)nativeModuleRegistry2.next();
        Systrace.beginSection( 0L, "createAndProcessCustomReactPackage" );

        try {
            this .processPackage(javaScriptModulesConfig , reactContext, nativeRegistryBuilder, jsModulesBuilder) ;
        } finally {
            Systrace.endSection(0L) ;
        }
    }

    ReactMarker.logMarker("PROCESS_PACKAGES_END" );
    ReactMarker.logMarker( "BUILD_NATIVE_MODULE_REGISTRY_START" );
    Systrace.beginSection( 0L, "buildNativeModuleRegistry" );

    NativeModuleRegistry nativeModuleRegistry1 ;
    try {
        nativeModuleRegistry1 = nativeRegistryBuilder.build();
    } finally {
        Systrace.endSection(0L );
        ReactMarker.logMarker( "BUILD_NATIVE_MODULE_REGISTRY_END" );
    }

    ReactMarker.logMarker("BUILD_JS_MODULE_CONFIG_START" );
    Systrace.beginSection( 0L, "buildJSModuleConfig" );

    JavaScriptModulesConfig javaScriptModulesConfig1 ;
    try {
        javaScriptModulesConfig1 = jsModulesBuilder.build();
    } finally {
        Systrace.endSection(0L );
        ReactMarker.logMarker( "BUILD_JS_MODULE_CONFIG_END" );
    }

    Object exceptionHandler = this .mNativeModuleCallExceptionHandler != null?this .mNativeModuleCallExceptionHandler: this.mDevSupportManager;
    com.facebook.react.bridge.CatalystInstanceImpl.Builder catalystInstanceBuilder = ( new com.facebook.react.bridge.CatalystInstanceImpl.Builder()).setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()).setJSExecutor(jsExecutor).setRegistry(nativeModuleRegistry1).setJSModulesConfig(javaScriptModulesConfig1).setJSBundleLoader(jsBundleLoader).setNativeModuleCallExceptionHandler((NativeModuleCallExceptionHandler)exceptionHandler) ;
    ReactMarker.logMarker( "CREATE_CATALYST_INSTANCE_START" );
    Systrace.beginSection( 0L, "createCatalystInstance" );

    CatalystInstanceImpl catalystInstance ;
    try {
        catalystInstance = catalystInstanceBuilder.build();
    } finally {
        Systrace.endSection(0L );
        ReactMarker.logMarker( "CREATE_CATALYST_INSTANCE_END" );
    }

    if (this.mBridgeIdleDebugListener != null) {
        catalystInstance.addBridgeIdleDebugListener(this.mBridgeIdleDebugListener) ;
    }

    reactContext.initializeWithInstance(catalystInstance);
    ReactMarker.logMarker( "RUN_JS_BUNDLE_START" );
    Systrace.beginSection( 0L, "runJSBundle" );

    try {
        catalystInstance.runJSBundle();
    } finally {
        Systrace.endSection(0L );
        ReactMarker.logMarker( "RUN_JS_BUNDLE_END" );
    }

    return reactContext;
}

代码很长,我们来分段分析。

第一步

com.facebook.react.bridge.JavaScriptModulesConfig.Builder jsModulesBuilder = new com.facebook.react.bridge.JavaScriptModulesConfig.Builder();

创建JavaScriptModulesConfig。

第二步

ReactApplicationContext reactContext = new ReactApplicationContext(this.mApplicationContext);

创建ReactApplicationContext上下文。

第三步

 try {
            CoreModulesPackage nativeModuleRegistry = new CoreModulesPackage(this, this.mBackBtnHandler, this.mUIImplementationProvider);
            this.processPackage(nativeModuleRegistry, reactContext, nativeRegistryBuilder, jsModulesBuilder);
        } finally {
            Systrace.endSection(0L);
        }

        Iterator nativeModuleRegistry2 = this.mPackages.iterator();

        while(nativeModuleRegistry2.hasNext()) {
            ReactPackage javaScriptModulesConfig = (ReactPackage)nativeModuleRegistry2.next();
            Systrace.beginSection(0L, "createAndProcessCustomReactPackage");

            try {
                this.processPackage(javaScriptModulesConfig, reactContext, nativeRegistryBuilder, jsModulesBuilder);
            } finally {
                Systrace.endSection(0L);
            }
        }

创建ReactPackage。ReactPackage主要通过createNativeModules、createJSModules和createViewManagers等API去创建本地模块,JS模块及视图组件等。ReactPackage分为framework的CoreModulesPackage和业务方可选的基础MainReactPackage,CoreModulesPackage封装了大部分通信,调试核心类,如UIManagerModule,这个负责控制Js层Dom到Native View的核心类;看下processPackage方法:

 private void processPackage(ReactPackage reactPackage, ReactApplicationContext reactContext, Builder nativeRegistryBuilder, com.facebook.react.bridge.JavaScriptModulesConfig.Builder jsModulesBuilder) {
        Iterator i$ = reactPackage.createNativeModules(reactContext).iterator();

        while(i$.hasNext()) {
            NativeModule jsModuleClass = (NativeModule)i$.next();
            nativeRegistryBuilder.add(jsModuleClass);
        }

        i$ = reactPackage.createJSModules().iterator();

        while(i$.hasNext()) {
            Class jsModuleClass1 = (Class)i$.next();
            jsModulesBuilder.add(jsModuleClass1);
        }

    }

很简单,拿到具体的native和JS的module把它们添加到对应的builder中。先是添加CoreModulesPackage中的module再添加我们自定义的module,先看CoreModulesPackage中的createNativeModules方法:

    public List<NativeModule> createNativeModules(ReactApplicationContext catalystApplicationContext) {
        Systrace.beginSection(0L, "createUIManagerModule");

        UIManagerModule uiManagerModule;
        try {
            List viewManagersList = this.mReactInstanceManager.createAllViewManagers(catalystApplicationContext);
            uiManagerModule = new UIManagerModule(catalystApplicationContext, viewManagersList, this.mUIImplementationProvider.createUIImplementation(catalystApplicationContext, viewManagersList));
        } finally {
            Systrace.endSection(0L);
        }

        return Arrays.asList(new NativeModule[]{new AnimationsDebugModule(catalystApplicationContext, this.mReactInstanceManager.getDevSupportManager().getDevSettings()), new AndroidInfoModule(), new DeviceEventManagerModule(catalystApplicationContext, this.mHardwareBackBtnHandler), new ExceptionsManagerModule(this.mReactInstanceManager.getDevSupportManager()), new Timing(catalystApplicationContext), new SourceCodeModule(this.mReactInstanceManager.getSourceUrl(), this.mReactInstanceManager.getDevSupportManager().getSourceMapUrl()), uiManagerModule, new DebugComponentOwnershipModule(catalystApplicationContext)});
    }

就是将UIManagerModule、AnimationsDebugModule等装到build中。
接着添加我们自定义的组件,以自定义Log为例,需要如下内容吗:

public class AppReactPackage implements ReactPackage{

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
        List<NativeModule> modules=new ArrayList<>();
        modules.add(new LogModule(reactApplicationContext));
        return modules;
    }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
        return Collections.emptyList();
    }
}

很简单,装到自定义的List中。

第四步

CatalystInstanceImpl catalystInstance;
        try {
            catalystInstance = catalystInstanceBuilder.build();
        } finally {
            Systrace.endSection(0L);
            ReactMarker.logMarker("CREATE_CATALYST_INSTANCE_END");
        }

创建CatalystInstance。CatalystInstance并不直接面向开发者,开发者通ReactInstanceManger间接操作CatalystInstance。CatalystInstance持有对ReactBridge的引用,主要通过ReactBridge这个JNI类去实现Java层与Js层的通信,ReactBridge由CatalystInstance的Constructor创建。同时初始化的时候调用了ReactQueueConfigurationSpec.createDefault创建了ReactNative通信的两个线程 JsQueueThread&NativeModulesQueueThread;
在这里ReactBridge由CatalystInstance的Constructor创建。看下它的构造函数:

private CatalystInstanceImpl(ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, NativeModuleRegistry registry, final JavaScriptModulesConfig jsModulesConfig, JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
        this.mPendingJSCalls = new AtomicInteger(0);
        this.mJsPendingCallsTitleForTrace = "pending_js_calls_instance" + sNextInstanceIdForTrace.getAndIncrement();
        this.mDestroyed = false;
        this.mJSToJavaCallsTeardownLock = new Object();
        this.mJavaToJSCallsTeardownLock = new Object();
        this.mInitialized = false;
        FLog.d("React", "Initializing React Bridge.");
        this.mReactQueueConfiguration = ReactQueueConfigurationImpl.create(ReactQueueConfigurationSpec, new CatalystInstanceImpl.NativeExceptionHandler(null));
        this.mBridgeIdleListeners = new CopyOnWriteArrayList();
        this.mJavaRegistry = registry;
        this.mJSModuleRegistry = new JavaScriptModuleRegistry(this, jsModulesConfig);
        this.mJSBundleLoader = jsBundleLoader;
        this.mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
        this.mTraceListener = new CatalystInstanceImpl.JSProfilerTraceListener(null);

        try {
            this.mBridge = (ReactBridge)this.mReactQueueConfiguration.getJSQueueThread().callOnQueue(new Callable() {
                public ReactBridge call() throws Exception {
                    Systrace.beginSection(0L, "initializeBridge");

                    ReactBridge var1;
                    try {
                        var1 = CatalystInstanceImpl.this.initializeBridge(jsExecutor, jsModulesConfig);
                    } finally {
                        Systrace.endSection(0L);
                    }

                    return var1;
                }
            }).get();
        } catch (Exception var8) {
            throw new RuntimeException("Failed to initialize bridge", var8);
        }
    }

注意到这行代码:

 this.mJSModuleRegistry = new JavaScriptModuleRegistry(this, jsModulesConfig);

这里通过jsModulesConfig(封装了module)创建了JSModuleRegistry。好了js注册表终于创建成功了。这里有两个问题,native注册表在哪创建呢,还有就是注册表什么时候传给js层呢。先留着这两个问题。
接下来看下initializeBridge方法:

private ReactBridge initializeBridge (JavaScriptExecutor jsExecutor, JavaScriptModulesConfig jsModulesConfig) {
    this .mReactQueueConfiguration.getJSQueueThread().assertIsOnThread() ;
    Assertions.assertCondition( this.mBridge == null, "initializeBridge should be called once" );
    Systrace.beginSection( 0L, "ReactBridgeCtor" );

    ReactBridge bridge ;
    try {
        bridge = new ReactBridge(jsExecutor, new CatalystInstanceImpl.NativeModulesReactCallback( null), this.mReactQueueConfiguration.getNativeModulesQueueThread()) ;
        this.mMainExecutorToken = bridge.getMainExecutorToken() ;
    } finally {
        Systrace.endSection(0L );
    }

    Systrace.beginSection(0L , "setBatchedBridgeConfig");

    try {
        bridge.setGlobalVariable("__fbBatchedBridgeConfig" , this.buildModulesConfigJSONProperty( this.mJavaRegistry, jsModulesConfig));
        bridge.setGlobalVariable( "__RCTProfileIsProfiling" , Systrace.isTracing( 0L)?"true" :"false") ;
    } finally {
        Systrace.endSection(0L );
    }

    this .mJavaRegistry.notifyReactBridgeInitialized(bridge) ;
    return bridge ;
}

ReactBridge将注册表信息存入与前端互通的全局变量 __fbBatchedBridgeConfig 中,使得Js层与Java层存在同样的模块注册表。bridge.setGlobalVariable是一个native函数。让我们猜一下下它的功能,就是用jsModulesConfig这个参数在js层中生成模块注册表,先看一下参数 buildModulesConfigJSONProperty的代码:

 private String buildModulesConfigJSONProperty(NativeModuleRegistry nativeModuleRegistry, JavaScriptModulesConfig jsModulesConfig) {
        StringWriter stringWriter = new StringWriter();
        JsonWriter writer = new JsonWriter(stringWriter);

        String ioe;
        try {
            writer.beginObject();
            writer.name("remoteModuleConfig");
            nativeModuleRegistry.writeModuleDescriptions(writer);
            writer.name("localModulesConfig");
            jsModulesConfig.writeModuleDescriptions(writer);
            writer.endObject();
            ioe = stringWriter.toString();
        } catch (IOException var14) {
            throw new RuntimeException("Unable to serialize JavaScript module declaration", var14);
        } finally {
            try {
                writer.close();
            } catch (IOException var13) {
                ;
            }

        }

        return ioe;
    }

看到JsonWriter就知道是把NativeModuleRegistry 和JavaScriptModulesConfig 转换成Json字符串,其中remoteModuleConfig指NativeModuleRegistry 信息,localModulesConfig指JavaScriptModulesConfig 信息。看下JavaScriptModulesConfig 的writeModuleDescriptions方法:

 public void writeModuleDescriptions(JsonWriter writer) throws IOException {
        writer.beginObject();
        Iterator i$ = this.mModules.iterator();

        while(i$.hasNext()) {
            JavaScriptModuleRegistration registration = (JavaScriptModuleRegistration)i$.next();
            writer.name(registration.getName()).beginObject();
            this.appendJSModuleToJSONObject(writer, registration);
            writer.endObject();
        }

        writer.endObject();
    }

看下appendJSModuleToJSONObject方法:

private void appendJSModuleToJSONObject(JsonWriter writer, JavaScriptModuleRegistration registration) throws IOException {
        writer.name("moduleID").value((long)registration.getModuleId());
        writer.name("methods").beginObject();
        Iterator i$ = registration.getMethods().iterator();

        while(i$.hasNext()) {
            Method method = (Method)i$.next();
            writer.name(method.getName()).beginObject();
            writer.name("methodID").value((long)registration.getMethodId(method));
            writer.endObject();
        }

        writer.endObject();
        if(registration.getModuleInterface().isAnnotationPresent(SupportsWebWorkers.class)) {
            writer.name("supportsWebWorkers").value(true);
        }

    }

从上代码可知生成的json字符串包含moduleID和methodID信息。NativeModuleRegistry 也同理,我们大概看下它的代码:

 void writeModuleDescriptions(JsonWriter writer) throws IOException {
        Systrace.beginSection(0L, "CreateJSON");

        try {
            writer.beginObject();
            Iterator i$ = this.mModuleTable.iterator();

            while(i$.hasNext()) {
                NativeModuleRegistry.ModuleDefinition moduleDef = (NativeModuleRegistry.ModuleDefinition)i$.next();
                writer.name(moduleDef.name).beginObject();
                writer.name("moduleID").value((long)moduleDef.id);
                writer.name("supportsWebWorkers").value(moduleDef.target.supportsWebWorkers());
                writer.name("methods").beginObject();

                for(int i = 0; i < moduleDef.methods.size(); ++i) {
                    NativeModuleRegistry.MethodRegistration method = (NativeModuleRegistry.MethodRegistration)moduleDef.methods.get(i);
                    writer.name(method.name).beginObject();
                    writer.name("methodID").value((long)i);
                    writer.name("type").value(method.method.getType());
                    writer.endObject();
                }

                writer.endObject();
                moduleDef.target.writeConstantsField(writer, "constants");
                writer.endObject();
            }

            writer.endObject();
        } finally {
            Systrace.endSection(0L);
        }
    }

接下来我们要找到setGlobalVariable的Native层代码,C++层代码我不太懂,这里参考了下别人的分析过程。大概过程是这样,首先入口是OnLoad.cpp。在其中找到如下代码:

void Bridge::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
  runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
    executor->setGlobalVariable(propName, jsonValue);
  });
}

都是塞进runOnExecutorQueue执行队列里面等待调用,最后回调到JSExecutor,而JSExecutor的实现类是JSCExecutor,最后来看看它的setGlobalVariable方法。

void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
  auto globalObject = JSContextGetGlobalObject(m_context);
  String jsPropertyName(propName.c_str());

  String jsValueJSON(jsonValue.c_str());
  auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);

  JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}

懂个大概吧,参数propName是从Java层传递过来的,相当于java代码中的__fbBatchedBridgeConfig和__RCTProfileIsProfiling。jsPropertyName方法就是buildModulesConfigJSONProperty封装好的对象。JSContextGetGlobalObject是WeiKit的方法,接下来会调用到js层的MessageQueue中:

const MessageQueue = require('MessageQueue');

const BatchedBridge = new MessageQueue(
  __fbBatchedBridgeConfig.remoteModuleConfig,
  __fbBatchedBridgeConfig.localModulesConfig,
);

生成两个映射表,从上面的代码我们己经分析过了,remoteModuleConfig是NativeModuleRegisty映射表内容。localModulesConfig则是JavaScriptModule内容。
到这里,js就生成了两张映射表了,这样java层和js层就都存在同样的映射表,相互通信就是通过它来实现。扯远了,回到createReactView。

第五步

try {
            catalystInstance.runJSBundle();
        } finally {
            Systrace.endSection(0L);
            ReactMarker.logMarker("RUN_JS_BUNDLE_END");
        }

调用catalystInstance.runJSBundle加载解析Jsbundle。
回到createReactView方法,看catalystInstance.runJSBundle:

public void runJSBundle() {
    try {
        this.mJSBundleHasLoaded = ((Boolean)this.mReactQueueConfiguration.getJSQueueThread().callOnQueue(new Callable() {
            public Boolean call() throws Exception {
                Assertions.assertCondition(!CatalystInstanceImpl.this.mJSBundleHasLoaded, "JS bundle was already loaded!");
                CatalystInstanceImpl.this.incrementPendingJSCalls();
                Systrace.beginSection(0L, "loadJSScript");

                try {
                    CatalystInstanceImpl.this.mJSBundleLoader.loadScript(CatalystInstanceImpl.this.mBridge);
                    Systrace.registerListener(CatalystInstanceImpl.this.mTraceListener);
                } catch (JSExecutionException var5) {
                    CatalystInstanceImpl.this.mNativeModuleCallExceptionHandler.handleException(var5);
                } finally {
                    Systrace.endSection(0L);
                }

                return Boolean.valueOf(true);
            }
        }).get()).booleanValue();
    } catch (Exception var2) {
        throw new RuntimeException(var2);
    }
}

调用catalystInstance.runJSBundle加载解析Jsbundle。假如在解析过程中出现Exception,统一交给NativeModuleCallExceptionHandler处理。
在创建完React Context后会执行ReactContextInitAsyncTask的onPostExecute。来看下onPostExecute的代码:

private void setupReactContext(ReactApplicationContext reactContext) {
        UiThreadUtil.assertOnUiThread();
        Assertions.assertCondition(this.mCurrentReactContext == null);
        this.mCurrentReactContext = (ReactContext)Assertions.assertNotNull(reactContext);
        CatalystInstance catalystInstance = (CatalystInstance)Assertions.assertNotNull(reactContext.getCatalystInstance());
        catalystInstance.initialize();
        this.mDevSupportManager.onNewReactContextCreated(reactContext);
        this.mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
        this.moveReactContextToCurrentLifecycleState();
        Iterator listeners = this.mAttachedRootViews.iterator();

        while(listeners.hasNext()) {
            ReactRootView arr$ = (ReactRootView)listeners.next();
            this.attachMeasuredRootViewToInstance(arr$, catalystInstance);
        }

        ReactInstanceEventListener[] var8 = new ReactInstanceEventListener[this.mReactInstanceEventListeners.size()];
        var8 = (ReactInstanceEventListener[])this.mReactInstanceEventListeners.toArray(var8);
        ReactInstanceEventListener[] var9 = var8;
        int len$ = var8.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            ReactInstanceEventListener listener = var9[i$];
            listener.onReactContextInitialized(reactContext);
        }

    }

这里主要实现两个功能,第一,调用catalystInstance.initialize()来创建NativeModuleRegistry,好啦,回答了一个问题了哈。

public void initialize() {
        UiThreadUtil.assertOnUiThread();
        Assertions.assertCondition(!this.mInitialized, "This catalyst instance has already been initialized");
        this.mInitialized = true;
        this.mJavaRegistry.notifyCatalystInstanceInitialized();
    }

第二,调用attachMeasuredRootView方法。将ReactRootView做为Root View传递给UIManagerModule,此后Js通过UIManager创建的View都会add到该View上。如下:

public void attachMeasuredRootView(ReactRootView rootView) {
    UiThreadUtil.assertOnUiThread();
    this.mAttachedRootViews.add(rootView) ;
    if( this.mReactContextInitAsyncTask == null && this.mCurrentReactContext != null) {
        this .attachMeasuredRootViewToInstance(rootView , this.mCurrentReactContext.getCatalystInstance()) ;
    }

}

再来看下attachMeasuredRootViewToInstance这个方法:

private void attachMeasuredRootViewToInstance(ReactRootView rootView , CatalystInstance catalystInstance) {
    UiThreadUtil.assertOnUiThread();
    rootView.removeAllViews() ;
    rootView.setId(- 1);
    UIManagerModule uiManagerModule = (UIManagerModule)catalystInstance.getNativeModule(UIManagerModule.class );
    int rootTag = uiManagerModule.addMeasuredRootView(rootView) ;
    Bundle launchOptions = rootView.getLaunchOptions() ;
    WritableMap initialProps = launchOptions != null?Arguments.fromBundle(launchOptions):Arguments.createMap();
    String jsAppModuleName = rootView.getJSModuleName() ;
    WritableNativeMap appParams = new WritableNativeMap();
    appParams.putDouble( "rootTag", ( double)rootTag);
    appParams.putMap( "initialProps" , initialProps);
    ((AppRegistry)catalystInstance.getJSModule(AppRegistry. class)).runApplication(jsAppModuleName, appParams) ;

在绑定完RootView后,通过CatalystInstance获取AppRegistry这个JSModule后,进一步调用runApplication启动Js Application。这个方法的最后用了我们的CatalystInstanceImpl的getJSModule方法,它会去调用JavaScriptModuleRegistry的getJSModule方法,获取对应的JavaScriptModule,也就是从注册表中获取对应的模块。这个地方很新颖,用的是用动态代理方式调用到 JavaScriptModule,具体看JavaScriptModuleInvocationHandler中的invoke方法。

@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  ExecutorToken executorToken = mExecutorToken.get();
  if (executorToken == null) {
    FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
    return null;
  }
  String tracingName = mModuleRegistration.getTracingName(method);
  mCatalystInstance.callFunction(
    executorToken,
      mModuleRegistration.getModuleId(),
      mModuleRegistration.getMethodId(method),
      Arguments.fromJavaArgs(args),
      tracingName);
  return null;
}

这里获取了调用了方法的moduleId,methodId和参数args,然后调用了CatalystInstanceImpl的callFunction去执行。callFunction也是一个native方法。跟上面的setGlobalVariable流程是一样的,调用的是JSCExecutor的callFunction方法。

void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
  // TODO:  Make this a first class function instead of evaling. #9317773
  std::vector<folly::dynamic> call{
    moduleId,
    methodId,
    std::move(arguments),
  };
  std::string calls = executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
  m_bridge->callNativeModules(*this, calls, true);
}

看下executeJSCallWithJSC方法:

static std::string executeJSCallWithJSC(
    JSGlobalContextRef ctx,
    const std::string& methodName,
    const std::vector<folly::dynamic>& arguments) {

  ...

  // Evaluate script with JSC
  folly::dynamic jsonArgs(arguments.begin(), arguments.end());
  auto js = folly::to<folly::fbstring>(
      "__fbBatchedBridge.", methodName, ".apply(null, ",
      folly::toJson(jsonArgs), ")");
  auto result = evaluateScript(ctx, String(js.c_str()), nullptr);
  return Value(ctx, result).toJSONString();
}

如上:

auto js = folly::to<folly::fbstring>(
      "__fbBatchedBridge.", methodName, ".apply(null, ",
      folly::toJson(jsonArgs), ")");

用于生成js语名,evaluateScript用于执行js语句。methodName的值为callFunctionReturnFlushedQueue,所以它会调用到MessageQueue.callFunctionReturnFlushedQueue方法,这时就到了js层了:

callFunctionReturnFlushedQueue(module, method, args) {
    guard(() => {
      this.__callFunction(module, method, args);
      this.__callImmediates();
    });

    return this.flushedQueue();
  }

var guard = (fn) => {
  try {
    fn();
  } catch (error) {
    ErrorUtils.reportFatalError(error);
  }
};

看下__callFunction方法:

_callFunction(module, method, args) {
    ...
    if (isFinite(module)) {
      method = this._methodTable[module][method];
      module = this._moduleTable[module];
    }
    ...
    var moduleMethods = this._callableModules[module];
    invariant(
      !!moduleMethods,
      'Module %s is not a registered callable module.',
      module
    );

    moduleMethods[method].apply(moduleMethods, args);
    ...
  }

通过moduleID和methodID来查询两张映射Table了,获取到了具体的moduleName和methodName,接着肯定要做调用Javascript对应组件了。这样就完成了java层调用js层的module了。说了这么多看下流程图吧:
这里写图片描述

js与java通信

这里还有一个问题就是反过来的调用 。js调用java的Module。
RN的js调java的流程具体就是是将对应的的参数(moduleId和methodId)push到一个messageQueue中,然后等待java层的事件来驱动它,当java层的事件传递过来时,js层把messageQueue中数据一次性回调给了给java层,最后再通过注册表去调用相应Module的方法。
这里以Toast为例。我们在js层给java层回调参数时会这么写:

 RCTToastAndroid.show(message, duration);

而RCTToastAndroid又是NativeModules里的一个属性,最终会调用MessageQueue.RemoteModules:

 let modulesConfig = this._genModulesConfig(remoteModules);
    this._genModules(modulesConfig);

remoteModules就是上面分析过的,NativeModuleRegistry映射表。看下_genModules方法。

_genModules(remoteModules) {
    remoteModules.forEach((config, moduleID) => {
      this._genModule(config, moduleID);
    });
  }

再看下_genModule方法:

_genModule(config, moduleID) {
    if (!config) {
      return;
    }

    let moduleName, constants, methods, asyncMethods;
    if (moduleHasConstants(config)) {
      [moduleName, constants, methods, asyncMethods] = config;
    } else {
      [moduleName, methods, asyncMethods] = config;
    }

    let module = {};
    methods && methods.forEach((methodName, methodID) => {
      const methodType =
        asyncMethods && arrayContains(asyncMethods, methodID) ?
          MethodTypes.remoteAsync : MethodTypes.remote;
      module[methodName] = this._genMethod(moduleID, methodID, methodType);
    });
    Object.assign(module, constants);

    if (!constants && !methods && !asyncMethods) {
      module.moduleID = moduleID;
    }

    this.RemoteModules[moduleName] = module;
    return module;
  }

主要调用_genMethod方法,它里面实现跳到了__nativeCall方法。所以,说了这么之所有的js最终都会调用到__nativeCall方法。

__nativeCall(module, method, params, onFail, onSucc) {

    ...

    this._callID++;

    this._queue[MODULE_IDS].push(module);
    this._queue[METHOD_IDS].push(method);
    this._queue[PARAMS].push(params);

    ...
  }

将ModuleID和MethodID和要传的参数push到_queue中。
当java事件驱动到来时,调用callFunctionReturnFlushedQueue方法:

callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
  this.__callFunction(module, method, args);
  this.__callImmediates();
});

return this.flushedQueue();
}

返回_queue。如上面分析过的,事件驱动到来会执行JSCExecutor的callFunction。最终会执行:

m_callback->onCallNativeModules(getTokenForExecutor(executor), parseMethodCalls(callJSON), isEndOfBatch);

m_callback真正的引用是PlatformBridgeCallback,直接看它的onCallNativeModules方法:

 virtual void onCallNativeModules(ExecutorToken executorToken, std::vector<MethodCall>&& calls, bool isEndOfBatch) override {
    executeCallbackOnCallbackQueueThread([executorToken, calls, isEndOfBatch] (ResolvedWeakReference& callback) {
      JNIEnv* env = Environment::current();
      for (auto& call : calls) {
        makeJavaCall(env, executorToken, callback, call);
        if (env->ExceptionCheck()) {
          return;
        }
      }
      if (isEndOfBatch) {
        signalBatchComplete(env, callback);
      }
    });
  }
  ...
  }

在回调队列线程中执行回调,被执行的回调方法里面对calls进行遍历,分别执行makeJavaCall并把多个执行结果放到一次回调给Native。

env->CallVoidMethod(
      callback,
      gCallbackMethod,
      static_cast<JExecutorTokenHolder*>(executorToken.getPlatformExecutorToken().get())->getJobj(),
      call.moduleId,
      call.methodId,
      newArray.get());
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
    bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(Lcom/facebook/react/bridge/ExecutorToken;IILcom/facebook/react/bridge/ReadableNativeArray;)V");

makeJavaCall将来自Javascript层的moduleId、methodId、args,被调用到Java层的ReactCallback的call方法里面。
java层中,JNI层调用的ReactCallback其实就是NativeModulesReactCallback对象,NativeModulesReactCallback是CatalystInstanceImpl的一个内部类,直接看它的call方法:

   public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
            CatalystInstanceImpl.this.mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
            synchronized(CatalystInstanceImpl.this.mJSToJavaCallsTeardownLock) {
                if(!CatalystInstanceImpl.this.mDestroyed) {
                    CatalystInstanceImpl.this.mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
                }
            }
        }

mJavaRegistry就是java层保存的NativeModuleRegistry映射表,这里就是通过Js传过来moduleId, methodId来匹配方法,看下它的call方法:

void call(CatalystInstance catalystInstance, ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
        NativeModuleRegistry.ModuleDefinition definition = (NativeModuleRegistry.ModuleDefinition)this.mModuleTable.get(moduleId);
        if(definition == null) {
            throw new RuntimeException("Call to unknown module: " + moduleId);
        } else {
            definition.call(catalystInstance, executorToken, methodId, parameters);
        }
    }

ModuleDefinition则是NativeModuleRegistry的一个内部类,mModuleTable是保持着NativeModule的映射表,通过get方法获得所有调用的Module,在这里就是ToastModule。看下ModuleDefinition的call方法:

 public void call(CatalystInstance catalystInstance, ExecutorToken executorToken, int methodId, ReadableNativeArray parameters) {
            NativeModuleRegistry.MethodRegistration method = (NativeModuleRegistry.MethodRegistration)this.methods.get(methodId);
            Systrace.beginSection(0L, method.tracingName);

            try {
                ((NativeModuleRegistry.MethodRegistration)this.methods.get(methodId)).method.invoke(catalystInstance, executorToken, parameters);
            } finally {
                Systrace.endSection(0L);
            }

        }

ModuleDefinition是NativeModule内方法信息的封装类,代码也在NativeModuleRegistry中。

   private static class MethodRegistration {
        public String name;
        public String tracingName;
        public NativeMethod method;

        public MethodRegistration(String name, String tracingName, NativeMethod method) {
            this.name = name;
            this.tracingName = tracingName;
            this.method = method;
        }
    }

NativeModule.NativeMethod对象,真正的实现则是JavaMethod类,所以this.methods.get(methodId)).method.invoke最终是调用javaMethod的invoke方法。

  public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {
            SystraceMessage.beginSection(0L, "callJavaModuleMethod").arg("method", this.mTraceName).flush();

            try {
                if(this.mJSArgumentsNeeded != parameters.size()) {
                    throw new NativeArgumentsParseException(BaseJavaModule.this.getName() + "." + this.mMethod.getName() + " got " + parameters.size() + " arguments, expected " + this.mJSArgumentsNeeded);
                }

                int i = 0;
                int jsArgumentsConsumed = 0;
                byte executorTokenOffset = 0;
                if(BaseJavaModule.this.supportsWebWorkers()) {
                    this.mArguments[0] = executorToken;
                    executorTokenOffset = 1;
                }

                try {
                    while(i < this.mArgumentExtractors.length) {
                        this.mArguments[i + executorTokenOffset] = this.mArgumentExtractors[i].extractArgument(catalystInstance, executorToken, parameters, jsArgumentsConsumed);
                        jsArgumentsConsumed += this.mArgumentExtractors[i].getJSArgumentsNeeded();
                        ++i;
                    }
                } catch (UnexpectedNativeTypeException var17) {
                    throw new NativeArgumentsParseException(var17.getMessage() + " (constructing arguments for " + BaseJavaModule.this.getName() + "." + this.mMethod.getName() + " at argument index " + this.getAffectedRange(jsArgumentsConsumed, this.mArgumentExtractors[i].getJSArgumentsNeeded()) + ")", var17);
                }

                try {
                    this.mMethod.invoke(BaseJavaModule.this, this.mArguments);
                } catch (IllegalArgumentException var14) {
                    throw new RuntimeException("Could not invoke " + BaseJavaModule.this.getName() + "." + this.mMethod.getName(), var14);
                } catch (IllegalAccessException var15) {
                    throw new RuntimeException("Could not invoke " + BaseJavaModule.this.getName() + "." + this.mMethod.getName(), var15);
                } catch (InvocationTargetException var16) {
                    if(var16.getCause() instanceof RuntimeException) {
                        throw (RuntimeException)var16.getCause();
                    }

                    throw new RuntimeException("Could not invoke " + BaseJavaModule.this.getName() + "." + this.mMethod.getName(), var16);
                }
            } finally {
                Systrace.endSection(0L);
            }

        }

上面代码中,从js层传过来的参数被封装到mArguments中,最后调用以下代码来完成 最终操作:

this.mMethod.invoke(BaseJavaModule.this, this.mArguments);

BaseJavaModule.this指代当前NativeModule对象的实例,如果是Toast组件的话就是ToastModule了,利用反射就找到了ToastModule模块。到此,js调用java流程就完成了。画了个流程图方便理解:
这里写图片描述

总结

最后来对这篇文章做一个总结。

  • 在程序启动的时候,首先会调用ReactActivity的onCreate函数中,我们会去创建一个ReactInstanceManagerImpl对象。通过ReactRootView的startReactApplication方法开启整个RN世界的大门。
  • 在这个方法中,我们会通过一个AsyncTask去创建ReactContext
  • 在创建ReactContext中,我们把我们自己注入和CoreModulesPackage通过processPackage方法将其中的各个modules注入到了对应的Registry中。最后通过CatalystInstanceImpl中的ReactBridge将NativeModule和JSModule注册表通过jni传输到了JS层。
  • java调用js时,会在ReactApplicationContext创建的时候存入注册表类JavaScriptModuleRegistry中,同时通过动态代理生成代理实例,并在代理拦截类JavaScriptModuleInvocationHandler中统一处理发向Javascript的所有通信请求。
  • JSCExecutor将所有来自Java层的通信请求封装成Javascript执行语句。
  • 接着在js层中的MessageQueue里匹配ModuleId和MethodId。找到调用模块。
  • 如果是js层调用java层,js最终都会调用__nativeCall方法,通过flushedQueue将this._queue返回给Bridger。
  • C++层调用PlatformBridgeCallback对象的onCallNativeModules方法,执行makeJavaCall方法,里面最终通过env->CallVoidMethod调用了Java层的方法。
  • 调用Java层NativeModulesReactCallback的call方法,通过moduleID从保存在其内部的NativeModule映射表,匹配到需要被执行的NativeModule对象,再通过methodID匹配到所要调用的方法。通过invoke反射方式执行NativeModule的方法。

ReactNative的源码流程就分析完了,当然还有很多不懂的地方,以后学习中再争取弄懂。

2018-02-27 11:38:16 qq_33323251 阅读数 1145
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57472 人正在学习 去看看 李宁

react native 修改第三方组件并保存

相信不少道友引用第三方组件时会感到不尽人意,需要手动修改第三方组价你的源代码,但是不可能把所有的npm依赖包都放到GitHub或者gitlab上面,从gitlab或者github上面重新down代码执行npm install后以前自己修改的第三方组件里的代码却没有了,因为自己只是在本地修改了人家的源代码,重新执行npm install 时会重新从github上面更新代码,这就了丢失自己修改过的代码,这时就需要用一种方法保存自己修改过后的第三方组件了,方法如下:

在gitlab或者github上面新建一个文件夹,专门存放已经修改过后的第三方组件,每修改一个第三方组件就把其放在里面,然后修改项目主目录下package.json文件里dependencies的代码:

修改之前:

"react-native-fs": "^2.3.3",
修改之后:

"react-native-fs": "file:./node_modules_local/react-native-fs",

修改之后的路径是你项目里 保存修改过后第三方组件的路径,注意在路径之前加上 file: 这是必不可少的

到这里就完工了,当你重新运行  npm install 时就会引用你修改过后的组件


2016-09-09 18:07:56 dongdong230 阅读数 1023
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57472 人正在学习 去看看 李宁

源码版本:”react-native”: “0.31.0”

React Native的Touchable*系列组件源码位于/node_modules/react-native/Labraries/Components/Touchable/下。
这里写图片描述

Touchable讲解

你将你的ResponderEventPlugin事件和Touchable相关联,Touchable就会去管理时间和结构,并会告诉你什么时候给用户反馈。
Touchable mixin帮助你处理”press”交互,他会分析元素结构,并观测其他响应抢夺touch。当需要给用户呈现反馈(弹跳/高亮/取消高亮)的时候,他会通知你的组件。

  • 当一个touch被激活(典型的如:高亮highlight)
  • 当一个touch取消deactivated(典型的如:取消高亮unhighlight)
  • 当一个touch被pressed -- 一个touch结束并仍在这个组件上,并且没有其它的元素(比如scroller)抢走touch响应。(典型的如:你让一个元素弹跳时)。

好的”轻击”操作并不像你想的那么简单。当你触摸时,在显示高亮前需要有一个细微的延迟。如果后面的touch移出了元素的边界,他就要取消高亮,但是同一touch回到了这个元素的边界内,它就要重新高亮。touch可以重复移入移出边界多次,每一次都会出发高亮,但是一个”press”仅在一个touch在元素内结束并且没有像scroller这样的元素抢走touch锁时,才会触发。

TouchableMixin

创建一个能够处理交互的组件要使用Touchable mixin,流程如下:

1.初始化Touchable状态

getInitialState: function() {
     return merge(this.touchableGetInitialState(), yourComponentState);
   }

2.选择绘制的组件,这个组件的触摸将开始组件的交互序列。在那个绘制的节点,转接所有Touchable的响应句柄(handlers)。你可以选择任意你喜欢的节点。选择的这个节点的的点击将开启一个交互的序列。

// In render function:
   return (
     <View
       onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
       onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
       onResponderGrant={this.touchableHandleResponderGrant}
       onResponderMove={this.touchableHandleResponderMove}
       onResponderRelease={this.touchableHandleResponderRelease}
       onResponderTerminate={this.touchableHandleResponderTerminate}>
       <View>
         尽管交互是在外层的节点触发,我们通常实现的逻辑是高亮当前这个内部节点的。
       </View>
     </View>
   );

3.你可以为这些事件创建自己的句柄,只要你在自己的句柄中调用这些touchable*句柄即可。
4.在你的组件类中实现句柄来给用户提供不同的反馈。你可以看看这些要实现的方法的相关文档来了解更多。

这里写代码片
touchableHandlePress: function() {
   this.performBounceAnimation();  // 或者写一些其它你想做的
},
touchableHandleActivePressIn: function() {
     this.beginHighlighting(...);  // 处理任何你想在激活时做的
   },
   touchableHandleActivePressOut: function() {
     this.endHighlighting(...);  // 处理任何你想在取消激活时做的
   },

5.文章后面还会有更多你可以实现的高级方法

 touchableGetHighlightDelayMS: function() {
     return 20;
   }
   // 经常使用预声明的常量
   touchableGetPressRectOffset: function() {
     return {top: 20, left: 20, right: 20, bottom: 100};
   }

Geometry结构

Touchable假定了有一个HitRect节点,PressRect是超出HitRect的抽象的盒子。
这里写图片描述
开启HitRect中事件将使得HitRect成为响应器。
HitRect是VisualRect周围的延伸,但是向下移动了。
“按压”后,过了一会并在松手之前,VisualRect将激活,这使得它有资格去高亮(只要按压保持在PressRect中)
图中红色区域允许touch从HitRect移到远处,同时保持高亮,并能press。可以通过touchableGetPressRectOffset()取自定义这个。

状态机

这里写图片描述

React-Native-源码分析

阅读数 1974

没有更多推荐了,返回首页