美文网首页
ReactNative Android 运行原理之

ReactNative Android 运行原理之

作者: 耗子_wo | 来源:发表于2021-02-06 23:03 被阅读0次

这一期我们来介绍下ReactNative Android 运行原理之<JAVA TO JavaScript>,由于整个流程步骤非常多,比较的复杂,但是有些朋友又想简单的去理解这个调用原理(至少我一开始也希望是这样的,但是发现非常困难),所以这一期我们来个轻松愉快的解说,我们通过这篇文章帮助大家通俗简单的去理解这个调用流程,让我们开始吧:

Java要能调用到JS需要在Java层注册JS模块,先来看下JS模块的注册过程。
系统注册了一些常用的JS模块,在CoreModulesPackage的createJSModules中注册了一些系统核心的JS模块,比如AppRegistry(RN组件注册模块)/RCTEventEmitter(事件发射模块)等。

class CoreModulesPackage implements ReactPackage{
  @Override
  public List<NativeModule> createNativeModules(
      ReactApplicationContext catalystApplicationContext) {
    ......
    return Arrays.<NativeModule>asList(
        new AnimationsDebugModule(
            catalystApplicationContext,
            mReactInstanceManager.getDevSupportManager().getDevSettings()),
        new AndroidInfoModule(),
        new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),
        new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager()),
        new Timing(catalystApplicationContext),
        new SourceCodeModule(
            mReactInstanceManager.getSourceUrl(),
            mReactInstanceManager.getDevSupportManager().getSourceMapUrl()),
        uiManagerModule,
        new DebugComponentOwnershipModule(catalystApplicationContext));
  }
  
  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Arrays.asList(
        DeviceEventManagerModule.RCTDeviceEventEmitter.class,
        JSTimersExecution.class,
        RCTEventEmitter.class,
        RCTNativeAppEventEmitter.class,
        AppRegistry.class,
        com.facebook.react.bridge.Systrace.class,
        DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
  }
}

public interface ReactPackage {

  /**
   * @param reactContext react application context that can be used to create modules
   * @return list of native modules to register with the newly created catalyst instance
   */
  List<NativeModule> createNativeModules(ReactApplicationContext reactContext);

  /**
   * @return list of JS modules to register with the newly created catalyst instance.
   *
   * IMPORTANT: Note that only modules that needs to be accessible from the native code should be
   * listed here. Also listing a native module here doesn't imply that the JS implementation of it
   * will be automatically included in the JS bundle.
   */
  List<Class<? extends JavaScriptModule>> createJSModules();

  /**
   * @return a list of view managers that should be registered with {@link UIManagerModule}
   */
  List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
}

以RCTEventEmitter这个JavaScriptModule模块为例来看下JS模块在Java层的实现。

public interface RCTEventEmitter extends JavaScriptModule {
  public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
  public void receiveTouches(
      String eventName,
      WritableArray touches,
      WritableArray changedIndices);
}

我们先来看看JavaScriptModulesConfig,他主要是对每个JavaScriptModule构造JavaScriptModuleRegistration,存放在mModules中

/**
 * Class stores configuration of javascript modules that can be used across the bridge
 */
public class JavaScriptModulesConfig {

  private final List<JavaScriptModuleRegistration> mModules;

  private JavaScriptModulesConfig(List<JavaScriptModuleRegistration> modules) {
    mModules = modules;
  }

  /*package*/ List<JavaScriptModuleRegistration> getModuleDefinitions() {
    return mModules;
  }

  /*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
    jg.writeStartObject();
    for (JavaScriptModuleRegistration registration : mModules) {
      jg.writeObjectFieldStart(registration.getName());
      appendJSModuleToJSONObject(jg, registration);
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

  private void appendJSModuleToJSONObject(
      JsonGenerator jg,
      JavaScriptModuleRegistration registration) throws IOException {
    jg.writeObjectField("moduleID", registration.getModuleId());
    jg.writeObjectFieldStart("methods");
    for (Method method : registration.getMethods()) {
      jg.writeObjectFieldStart(method.getName());
      jg.writeObjectField("methodID", registration.getMethodId(method));
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

  public static class Builder {

    private int mLastJSModuleId = 0;
    private List<JavaScriptModuleRegistration> mModules =
        new ArrayList<JavaScriptModuleRegistration>();

    public Builder add(Class<? extends JavaScriptModule> moduleInterfaceClass) {
      int moduleId = mLastJSModuleId++;
      mModules.add(new JavaScriptModuleRegistration(moduleId, moduleInterfaceClass));
      return this;
    }

    public JavaScriptModulesConfig build() {
      return new JavaScriptModulesConfig(mModules);
    }
  }
}
/**
 * Registration info for a {@link JavaScriptModule}. 
    Maps its methods to method ids.
 */
class JavaScriptModuleRegistration {

  private final int mModuleId;
  private final Class<? extends JavaScriptModule> mModuleInterface;
  private final Map<Method, Integer> mMethodsToIds;
  private final Map<Method, String> mMethodsToTracingNames;

  JavaScriptModuleRegistration(int moduleId, Class<? extends JavaScriptModule> moduleInterface) {
    mModuleId = moduleId;
    mModuleInterface = moduleInterface;

    mMethodsToIds = MapBuilder.newHashMap();
    mMethodsToTracingNames = MapBuilder.newHashMap();
    final Method[] declaredMethods = mModuleInterface.getDeclaredMethods();
    Arrays.sort(declaredMethods, new Comparator<Method>() {
      @Override
      public int compare(Method lhs, Method rhs) {
        return lhs.getName().compareTo(rhs.getName());
      }
    });

    // Methods are sorted by name so we can dupe check and have obvious ordering
    String previousName = null;
    for (int i = 0; i < declaredMethods.length; i++) {
      Method method = declaredMethods[i];
      String name = method.getName();
      Assertions.assertCondition(
          !name.equals(previousName),
          "Method overloading is unsupported: " + mModuleInterface.getName() + "#" + name);
      previousName = name;

      mMethodsToIds.put(method, i);
      mMethodsToTracingNames.put(method, "JSCall__" + getName() + "_" + method.getName());
    }
  }
  ......
}

到这里就有了所有的JavaScriptModules,并且都扫描存放在JavaScriptModulesConfig中,那么RN框架是怎么使用的?我们再来看看ReactInstanceManagerImpl,他将JavaScriptModulesConfig用来实例化catalystInstance,这个是三方通信的中转站。

 //createCatalystInstance---Java/JS/C++三方通信总管
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new  CatalystInstanceImpl.Builder()
        .setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        .setJSModulesConfig(javaScriptModulesConfig)
        .setJSBundleLoader(jsBundleLoader)
        .setNativeModuleCallExceptionHandler(exceptionHandler);

Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
    CatalystInstance catalystInstance;
    try {
      catalystInstance = catalystInstanceBuilder.build();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

再接着看看实现类CatalystInstanceImpl:

private CatalystInstanceImpl(
      final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry registry,
      final JavaScriptModulesConfig jsModulesConfig,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    ...
    mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstanceImpl.this, jsModulesConfig);
    ...
  }

再接下来需要到JavaScriptModuleRegistry中看看:


/*package*/ class JavaScriptModuleRegistry {

  private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;

  public JavaScriptModuleRegistry(
      CatalystInstanceImpl instance,
      JavaScriptModulesConfig config) {
    mModuleInstances = new HashMap<>();
    for (JavaScriptModuleRegistration registration : config.getModuleDefinitions()) {
      Class<? extends JavaScriptModule> moduleInterface = registration.getModuleInterface();
      JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
          moduleInterface.getClassLoader(),
          new Class[]{moduleInterface},
          new JavaScriptModuleInvocationHandler(instance, registration));

      mModuleInstances.put(moduleInterface, interfaceProxy);
    }
  }

  public <T extends JavaScriptModule> T getJavaScriptModule(Class<T> moduleInterface) {
    return (T) Assertions.assertNotNull(
        mModuleInstances.get(moduleInterface),
        "JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
  }

  private static class JavaScriptModuleInvocationHandler implements InvocationHandler {

    private final CatalystInstanceImpl mCatalystInstance;
    private final JavaScriptModuleRegistration mModuleRegistration;

    public JavaScriptModuleInvocationHandler(
        CatalystInstanceImpl catalystInstance,
        JavaScriptModuleRegistration moduleRegistration) {
      mCatalystInstance = catalystInstance;
      mModuleRegistration = moduleRegistration;
    }

    @Override
    public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      String tracingName = mModuleRegistration.getTracingName(method);
      mCatalystInstance.callFunction(
          mModuleRegistration.getModuleId(),
          mModuleRegistration.getMethodId(method),
          Arguments.fromJavaArgs(args),
          tracingName);
      return null;
    }
  }
}

很明显这里就是对每个接口通过动态代理实例化一个代理对象,调用JavaScriptModule里面的方法(Java调用JavaScript方法)其实就是统一分发到JavaScriptModuleInvocationHandler中的invoke进行处理,在invoke方法中调用CatalystInstance.callFunction,CatalystInstance是一个接口,实现类CatalystInstanceImpl,看到CatalystInstanceImpl中的callFunction:

/* package */ void callFunction(
      final int moduleId,
      final int methodId,
      final NativeArray arguments,
      final String tracingName) {
    if (mDestroyed) {
      FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
      return;
    }

    incrementPendingJSCalls();

    final int traceID = mTraceID++;
    Systrace.startAsyncFlow(
        Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
        tracingName,
        traceID);

    mCatalystQueueConfiguration.getJSQueueThread().runOnQueue(
        new Runnable() {
          @Override
          public void run() {
            mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();

            Systrace.endAsyncFlow(
                Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
                tracingName,
                traceID);

            if (mDestroyed) {
              return;
            }

            Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, tracingName);
            try {
              Assertions.assertNotNull(mBridge).callFunction(moduleId, methodId, arguments);
            } finally {
              Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
            }
          }
        });
  }

通过这句话:Assertions.assertNotNull(mBridge).callFunction(moduleId, methodId, arguments); 就知道所有Java TO Javascript层的通信请求都是走的ReactBridge.callFunction,其实就是在JSQueueThread线程中调用ReactBridge的callFunction,是Native函数,到这里就从Java转到C++层了

/**
 * Interface to the JS execution environment and means of transport for messages Java<->JS.
 */
@DoNotStrip
public class ReactBridge extends Countable {

  /* package */ static final String REACT_NATIVE_LIB = "reactnativejni";

  static {
    SoLoader.loadLibrary(REACT_NATIVE_LIB);
  }

  private final ReactCallback mCallback;
  private final JavaScriptExecutor mJSExecutor;
  private final MessageQueueThread mNativeModulesQueueThread;

  /**
   * @param jsExecutor the JS executor to use to run JS
   * @param callback the callback class used to invoke native modules
   * @param nativeModulesQueueThread the MessageQueueThread the callbacks should be invoked on
   */
  public ReactBridge(
      JavaScriptExecutor jsExecutor,
      ReactCallback callback,
      MessageQueueThread nativeModulesQueueThread) {
    mJSExecutor = jsExecutor;
    mCallback = callback;
    mNativeModulesQueueThread = nativeModulesQueueThread;
    initialize(jsExecutor, callback, mNativeModulesQueueThread);
  }

  @Override
  public void dispose() {
    mJSExecutor.close();
    mJSExecutor.dispose();
    super.dispose();
  }

  public void handleMemoryPressure(MemoryPressure level) {
    switch (level) {
      case MODERATE:
        handleMemoryPressureModerate();
        break;
      case CRITICAL:
        handleMemoryPressureCritical();
        break;
      default:
        throw new IllegalArgumentException("Unknown level: " + level);
    }
  }

  private native void initialize(
      JavaScriptExecutor jsExecutor,
      ReactCallback callback,
      MessageQueueThread nativeModulesQueueThread);

  /**
   * All native functions are not thread safe and appropriate queues should be used
   */
  public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
  public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
  public native void callFunction(int moduleId, int methodId, NativeArray arguments);
  public native void invokeCallback(int callbackID, NativeArray arguments);
  public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
  public native boolean supportsProfiling();
  public native void startProfiler(String title);
  public native void stopProfiler(String title, String filename);
  private native void handleMemoryPressureModerate();
  private native void handleMemoryPressureCritical();
}

我们先不急着往下去跟踪,因为在调用之前初始化的时候会涉及到一些重要参数传递的过程,我们先来看看再回头看看ReactBridge在CatalystInstanceImpl里面的初始化,初始化会ReactBridge调用setGlobalVariable,这是个Native函数,是在C++层注册用的,我们先分析下这个函数buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig)做了什么。

private CatalystInstanceImpl(
      final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry registry,
      final JavaScriptModulesConfig jsModulesConfig,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    ......
    try {
      mBridge = mCatalystQueueConfiguration.getJSQueueThread().callOnQueue(
          new Callable<ReactBridge>() {
            @Override
            public ReactBridge call() throws Exception {
              Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
              try {
                return initializeBridge(jsExecutor, jsModulesConfig);
              } finally {
                Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
              }
            }
          }).get(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    } catch (Exception t) {
      throw new RuntimeException("Failed to initialize bridge", t);
    }
  }

  private ReactBridge initializeBridge(
      JavaScriptExecutor jsExecutor,
      JavaScriptModulesConfig jsModulesConfig) {
    mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
    Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
    ReactBridge bridge;
    try {
      bridge = new ReactBridge(
          jsExecutor,
          new NativeModulesReactCallback(),
          mCatalystQueueConfiguration.getNativeModulesQueueThread());
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
    try {
      bridge.setGlobalVariable(
          "__fbBatchedBridgeConfig",
          buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
      bridge.setGlobalVariable(
          "__RCTProfileIsProfiling",
          Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    return bridge;
  }

在CatalystInstanceImpl中的实现,其实就是写一个JSON字符串,有两个字段"remoteModuleConfig"和"localModulesConfig",分别对应NativeModule(Native提供给JS调用)和JSModule(JS提供给Native调用)

private String buildModulesConfigJSONProperty(
      NativeModuleRegistry nativeModuleRegistry,
      JavaScriptModulesConfig jsModulesConfig) {
    JsonFactory jsonFactory = new JsonFactory();
    StringWriter writer = new StringWriter();
    try {
      JsonGenerator jg = jsonFactory.createGenerator(writer);
      jg.writeStartObject();
      jg.writeFieldName("remoteModuleConfig");
      nativeModuleRegistry.writeModuleDescriptions(jg);
      jg.writeFieldName("localModulesConfig");
      jsModulesConfig.writeModuleDescriptions(jg);
      jg.writeEndObject();
      jg.close();
    } catch (IOException ioe) {
      throw new RuntimeException("Unable to serialize JavaScript module declaration", ioe);
    }
    return writer.getBuffer().toString();
  }

再看看localModulesConfig都写入了什么,其实就是往JSON字符创中先写入接口名、moduleID、methods(方法名、methodID)等。

/*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
    jg.writeStartObject();
    for (JavaScriptModuleRegistration registration : mModules) {
      jg.writeObjectFieldStart(registration.getName());
      appendJSModuleToJSONObject(jg, registration);
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

  private void appendJSModuleToJSONObject(
      JsonGenerator jg,
      JavaScriptModuleRegistration registration) throws IOException {
    jg.writeObjectField("moduleID", registration.getModuleId());
    jg.writeObjectFieldStart("methods");
    for (Method method : registration.getMethods()) {
      jg.writeObjectFieldStart(method.getName());
      jg.writeObjectField("methodID", registration.getMethodId(method));
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

我们已经想到了buildModulesConfigJSONProperty把所有的JavaScriptModule模块以moduleID+methodID形式生成JSON字符串,通过setGlobalVariable把JSON字符串预先存入了ReactBridge中,如下:

 bridge.setGlobalVariable(
          "__fbBatchedBridgeConfig",
          buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));

我们再来看看JNI的注册部分,JNI入口react/jni/OnLoad.cpp,通过RegisterNatives方式注册的,JNI_OnLoad里面注册了setGlobalVariable和callFunction等native本地方法

//jni/react/jni/OnLoad.cpp
namespace bridge {
  ......

static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstring jsonValue) {
  auto bridge = extractRefPtr<Bridge>(env, obj);
  bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
}
  ...
} // namespace bridge

extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  return initialize(vm, [] {
    // get the current env
    JNIEnv* env = Environment::current();
    registerNatives("com/facebook/react/bridge/ReadableNativeMap$ReadableNativeMapKeySetIterator", {
    ......
    registerNatives("com/facebook/react/bridge/ReactBridge", {
        makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
        makeNativeMethod(
          "loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
          bridge::loadScriptFromAssets),
        makeNativeMethod("loadScriptFromFile", bridge::loadScriptFromFile),
        makeNativeMethod("callFunction", bridge::callFunction),
        makeNativeMethod("invokeCallback", bridge::invokeCallback),
        makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
        makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
        makeNativeMethod("startProfiler", bridge::startProfiler),
        makeNativeMethod("stopProfiler", bridge::stopProfiler),
        makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
        makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),

    });
}

跟踪setGlobalVariable函数调用:

void Bridge::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
  m_threadState->setGlobalVariable(propName, jsonValue);
}

继续跟踪:

//jni/react/Bridge.cpp
class JSThreadState {
public:
  JSThreadState(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Bridge::Callback&& callback) :
    m_callback(callback)
  {
    m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON, bool isEndOfBatch) {
      m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
    });
  }

  void setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
    m_jsExecutor->setGlobalVariable(propName, jsonValue);
  }

private:
  std::unique_ptr<JSExecutor> m_jsExecutor;
  Bridge::Callback m_callback;
};

最终调用JSExecutor类的setGlobalVariable:

//jni/react/JSCExecutor.cpp
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);
}

我们这里必须要给大家解释一个东西也就是JavaScriptCore,我们知道JavaScript是解释执行的,他的执行引擎在Android默认的就是JavaScriptCore,而JavaScriptCore在Android架构中就是在framework层,基本上是C/C++实现,另外JavaScriptCore为了外部环境能够执行JavaScript提供了一套API接口给外部去动态执行JavaScript,这也是一套C++接口,这也是我们为什么要进入JNI的原因,而我们刚刚看到的 JSContextGetGlobalObjectJSObjectSetProperty 就是两个JavaScriptCore的API的两个暴露出来的接口

现在我们知道了JSCExecutor::setGlobalVariable最后给JS的全局变量设置一个属性__fbBatchedBridgeConfig,这个属性的值就是JavaScriptModule,接着再看下callFunction:

//jni/react/JSCExecutor.cpp
std::string JSCExecutor::callFunction(const double moduleId, const double methodId, const folly::dynamic& arguments) {
  // TODO:  Make this a first class function instead of evaling. #9317773
  std::vector<folly::dynamic> call{
    (double) moduleId,
    (double) methodId,
    std::move(arguments),
  };
  return executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
}

static std::string executeJSCallWithJSC(
    JSGlobalContextRef ctx,
    const std::string& methodName,
    const std::vector<folly::dynamic>& arguments) {
  #ifdef WITH_FBSYSTRACE
  FbSystraceSection s(
      TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.executeJSCall",
      "method", methodName);
  #endif

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

__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})的形式拼接一个apply的Javascript执行语句,最后调用的evaluateScript的来执行这个语句,完成Bridge向Javascript的调用,而evaluateScript又是一个JavaScriptCore暴露出来的API,他的作用就是执行一段JavaScript代码

来看一下JS层的BatchedBridge.js:

//Libraries/BatchedBridge/BatchedBridge.js
const MessageQueue = require('MessageQueue');

const BatchedBridge = new MessageQueue(
  __fbBatchedBridgeConfig.remoteModuleConfig,   //Native(Java)模块
  __fbBatchedBridgeConfig.localModulesConfig,   //JS模块
);

// TODO: Move these around to solve the cycle in a cleaner way.

const Systrace = require('Systrace');
const JSTimersExecution = require('JSTimersExecution');

BatchedBridge.registerCallableModule('Systrace', Systrace);
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);

if (__DEV__) {
  BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
}

Object.defineProperty(global, '__fbBatchedBridge', { value: BatchedBridge });

module.exports = BatchedBridge;

我们再来看看JavaScript端:刚才我们分析了由于__fbBatchedBridgeConfig对象是被直接定义成Global全局对象的属性,类似于window对象,__fbBatchedBridgeConfig对象里又有两个属性:remoteModuleConfig和localModulesConfig,分别对应Java模块和JS模块。而且还看到了定义了另一个全局属性:__fbBatchedBridge,而这个全局属性的值就是MessageQueue对象

我们上文提到的 __fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments}) 这个表达式里面__fbBatchedBridge就是他在这里赋的值

我们再看看callFunctionReturnFlushedQueue函数:

class MessageQueue {

 constructor(remoteModules, localModules) {
   this.RemoteModules = {};

   this._callableModules = {};
   this._queue = [[], [], [], 0];
   this._moduleTable = {};
   this._methodTable = {};
   this._callbacks = [];
   this._callbackID = 0;
   this._callID = 0;
   this._lastFlush = 0;
   this._eventLoopStartTime = new Date().getTime();

   [
     'invokeCallbackAndReturnFlushedQueue',
     'callFunctionReturnFlushedQueue',
     'flushedQueue',
   ].forEach((fn) => this[fn] = this[fn].bind(this));

   let modulesConfig = this._genModulesConfig(remoteModules);
   this._genModules(modulesConfig);
   localModules && this._genLookupTables(
     this._genModulesConfig(localModules),this._moduleTable, this._methodTable
   );

   this._debugInfo = {};
   this._remoteModuleTable = {};
   this._remoteMethodTable = {};
   this._genLookupTables(
     modulesConfig, this._remoteModuleTable, this._remoteMethodTable
   );
 }

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

   return this.flushedQueue();
 }
 
 _genLookupTables(modulesConfig, moduleTable, methodTable) {
   modulesConfig.forEach((config, moduleID) => {
     this._genLookup(config, moduleID, moduleTable, methodTable);
   });
 }

 _genLookup(config, moduleID, moduleTable, methodTable) {
   if (!config) {
     return;
   }

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

   moduleTable[moduleID] = moduleName;
   methodTable[moduleID] = Object.assign({}, methods);
 }
}

module.exports = MessageQueue;

里面__callFunction是最后一步了:

 __callFunction(module: string, method: string, args: any[]): void {
    this._lastFlush = Date.now();
    this._eventLoopStartTime = this._lastFlush;
    if (__DEV__ || this.__spy) {
      Systrace.beginEvent(`${module}.${method}(${stringifySafe(args)})`);
    } else {
      Systrace.beginEvent(`${module}.${method}(...)`);
    }
    if (this.__spy) {
      this.__spy({type: TO_JS, module, method, args});
    }
    const moduleMethods = this.getCallableModule(module);
    invariant(
      !!moduleMethods,
      'Module %s is not a registered callable module (calling %s)',
      module,
      method,
    );
    invariant(
      !!moduleMethods[method],
      'Method %s does not exist on module %s',
      method,
      module,
    );
    moduleMethods[method].apply(moduleMethods, args);
    Systrace.endEvent();
  }

相信大家对其中 moduleMethods[method].apply(moduleMethods, args); 这句话应该不陌生,大意就是运行moduleMethods的method方法,而moduleMethods又是怎么来的呢:const moduleMethods = this.getCallableModule(module);

  getCallableModule(name: string): any | null {
    const getValue = this._lazyCallableModules[name];
    return getValue ? getValue() : null;
  }

而_lazyCallableModules又是谁添加的呢,你可以这样理解,每一个想暴露的给Native的模块都会注册添加,当然也包括我们上面分析的RCTEventEmitter,我们看看RCTEventEmitter.js

//Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
var BatchedBridge = require('BatchedBridge');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');

BatchedBridge.registerCallableModule(
  'RCTEventEmitter',
  ReactNativeEventEmitter
);

// Completely locally implemented - no native hooks.
module.exports = ReactNativeEventEmitter;

其中registerCallableModule很简单就是往_lazyCallableModules去添加对象:

 registerCallableModule(name: string, module: Object) {
    this._lazyCallableModules[name] = () => module;
  }

我们再来看看我们在JAVA方法中使用的emit函数对应是怎么执行的啊

...
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
...
private void sendEvent(ReactContext reactContext,
                       String eventName,
                       @Nullable WritableMap params) {
  reactContext
      .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
      .emit(eventName, params);
}
...
WritableMap params = Arguments.createMap();
params.putString("eventProperty", "someValue");
...
sendEvent(reactContext, "EventReminder", params);

对应的JavaScript端:

  emit(eventType: string) {
    const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
    if (subscriptions) {
      for (let i = 0, l = subscriptions.length; i < l; i++) {
        const subscription = subscriptions[i];

        // The subscription may have been removed during this event loop.
        if (subscription && subscription.listener) {
          this._currentSubscription = subscription;
          subscription.listener.apply(
            subscription.context,
            Array.prototype.slice.call(arguments, 1),
          );
        }
      }
      this._currentSubscription = null;
    }
  }

稍微解释一下,大致就是通过eventType去或者一个或多个订阅者,然后遍历这些订阅者里面的subscription.listener,然后通过下面的语句去执行这些早就注册的listener

subscription.listener.apply(
            subscription.context,
            Array.prototype.slice.call(arguments, 1),
          ); 

最后上一段代码给大家回忆下JavaScript段怎么使用EventEmitter的

import { NativeEventEmitter, NativeModules } from 'react-native';
// ...

  componentDidMount() {
    // ...
    const eventEmitter = new NativeEventEmitter(NativeModules.ToastExample);
    this.eventListener = eventEmitter.addListener('EventReminder', (event) => {
       console.log(event.eventProperty) // "someValue"
    };
    // ...
  }
  componentWillUnmount() {
    this.eventListener.remove(); // 组件卸载时记得移除监听事件
  }

通过addListener来注册一个监听函数等待JAVA端的调用,里面的第一个参数就是订阅者的名字,第二个参数就是订阅者要执行的方法

既然都说到这里了,我们就再给大家把最后的调用关系串起来再分析下吧,JAVA端我们再看看这个发送事件的函数:

  reactContext
      .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
      .emit(eventName, params);

reactContext就是一个ReactContext对象,再看看他的getJSModule方法是什么呢:

  /**
   * @return handle to the specified JS module for the CatalystInstance associated with this Context
   */
  public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
    if (mCatalystInstance == null) {
      if (mDestroyed) {
        throw new IllegalStateException(LATE_JS_ACCESS_EXCEPTION_MESSAGE);
      }
      throw new IllegalStateException(EARLY_JS_ACCESS_EXCEPTION_MESSAGE);
    }
    return mCatalystInstance.getJSModule(jsInterface);
  }

就是调用了 mCatalystInstance.getJSModule(jsInterface); 去获取一个JavaScriptModule,mCatalystInstance其实是一个CatalystInstanceImpl的对象,再看看他的getJSModule方法是什么:

  @Override
  public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
    return mJSModuleRegistry.getJavaScriptModule(this, jsInterface);
  }

mJSModuleRegistry就是上面我们提及的JavaScriptModuleRegistry对象,大家可以往上翻翻,这个就是调用到了他的getJavaScriptModule方法返回了一个动态代理产生的JavaScriptModule对象,这样的话所有的调用都形成了一个闭环了

我们最后用几句话来总结整个调用过程来帮助大家总结下:

  • 一开始所有的NativeModule和JSModule在ReactInstanceManagerImpl中注册,所有的JavaScriptModules,并且都扫描存放在JavaScriptModulesConfig中,ReactInstanceManagerImpl将JavaScriptModulesConfig用来实例化catalystInstance;

  • catalystInstance的buildModulesConfigJSONProperty方法把JavaScriptModulesConfig,NativeModuleRegistry(所有的JavaScriptModule与NativeModule)都写入了__fbBatchedBridgeConfig全局对象里的两个属性:remoteModuleConfig和localModulesConfig,分别对应Java模块和JS模块,并且把这个通过Webkit的接口属性注入到了C++层的里面方便调用(因为JavaScript最终执行是在C++层,因为Android里面的WebKit,JavaScriptCore是在library的C++层,写入的是通过JavascriptCore提供接口实现)

  • Java调用JS方法时统一分发到JavaScriptModuleInvocationHandler中的invoke进行处理,在invoke中其实就是调用mCatalystInstance.callFunction->ReactBridge.callFunction->最终__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})的形式拼接一个apply的Javascript执行语句,最后调用jni/react/JSCHelpers.cpp的evaluateScript来执行(最终调用到JavascriptCore来执行这句JS)

  • JS端的__fbBatchedBridge就是BatchedBridge的属性,BatchedBridge就是一个MessageQueue对象,他在初始化的时候存放了__fbBatchedBridgeConfig的remoteModuleConfig(Native模块)与localModulesConfig(JS模块)最后调用到this.__callFunction(module, method, args)里面的moduleMethods[method].apply(moduleMethods, args)来进行调用,moduleMethods的来源就是被调用的JavaScript层通过registerCallableModule方法写入的属性(实现Native到JavaScript步骤里面JS的模块会做这个步骤)

好了,我们介绍的ReactNative Android 运行原理之<JAVA TO JavaScript>就告一段落了,里面涉及额部分代码有些是新版本的大家可能有点对不上,但是大致理解里面的思路就行,我们下一篇介绍 ReactNative Android 运行原理之<JavaScript TO JAVA>,欢迎敬请期待···

网友评论

      本文标题:ReactNative Android 运行原理之

      本文链接:https://www.haomeiwen.com/subject/wygdtltx.html