RN中Js如何调用Native的代码

RN中Js如何调用Native的代码_第1张图片
js调用java.png

react-native.js 其实就是声明了ReactNative提供的可以在js中使用的各种模块。

var ReactNative = Object.assign(Object.create(require('React')), {
  ...
  ToastAndroid: require('ToastAndroid'),
});
module.exports = ReactNative;

ToastAndroid.android.js show方法其实就是调用了RCTToastAndroid.show(message, duration)

var RCTToastAndroid = require('NativeModules').ToastAndroid;
var ToastAndroid = {
  SHORT: RCTToastAndroid.SHORT,
  LONG: RCTToastAndroid.LONG,
  show: function (
     message: string,
     duration: number
  ): void {
     RCTToastAndroid.show(message, duration);
  },
};

NativeModules.js RCTToastAndroid是定义在NativeModules中的

var NativeModules = require('BatchedBridge').RemoteModules;
var nativeModulePrefixNormalizer = require('nativeModulePrefixNormalizer');
nativeModulePrefixNormalizer(NativeModules);
module.exports = NativeModules;

BatchedBridge.js 最终,所有的模块都是来自BatchedBridge,它做的事情就是构造一个MessageQueue对象。

let MessageQueue = require('MessageQueue');
let BatchedBridge = new MessageQueue(
  __fbBatchedBridgeConfig.remoteModuleConfig,
  __fbBatchedBridgeConfig.localModulesConfig,
);
module.exports = BatchedBridge;

ToastAndroid.show方法其实最终调用的是MessageQueue.ToastAndroid.show方法。


__fbBatchedBridgeConfig 是一个全局js变量,它是在CatalystInstance.java中声明赋值的,通过调用ReactBridge.setGlobalVariable方法。setGlobalVariable是在Jni中声明的方法,最终会调用JavaScriptCore,把Java中定义的JSON字符串,赋值给js的全局对象__fbBatchedBridgeConfig,这个对象会有两个属性remoteModuleConfig和localModulesConfig。

__fbBatchedBridgeConfig.remoteModuleConfig,
__fbBatchedBridgeConfig.localModulesConfig,

CatalystInstanceImpl.java

private native void initializeBridge(
    ReactCallback callback,
    JavaScriptExecutor jsExecutor,
    MessageQueueThread jsQueue,
    MessageQueueThread moduleQueue,
    Collection javaModules,
    Collection cxxModules);
private void initializeBridge(
    JavaScriptExecutor jsExecutor,
    NativeModuleRegistry registry,
    JavaScriptModulesConfig jsModulesConfig,
    JSBundleLoader jsBundleLoader) {
  mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
  Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
  mBridge = new ReactBridge(
      jsExecutor,
      new NativeModulesReactCallback(),
      mCatalystQueueConfiguration.getNativeModulesQueueThread());
  mBridge.setGlobalVariable(
      "__fbBatchedBridgeConfig",
      buildModulesConfigJSONProperty(registry, jsModulesConfig));
  jsBundleLoader.loadScript(mBridge);
}

__fbBatchedBridgeConfig.remoteModuleConfig 代表的是Java中定义的一些模块,这些模块可以在js中被调用。格式如下:

{
  "remoteModuleConfig": {
    "Logger": {
      "constants": { /* If we had exported constants... */ },
      "moduleID": 1,
      "methods": {
        "requestPermissions": {
          "type": "remote",
          "methodID": 1
        }
      }
    }
  }
}
{
  'ToastAndroid': {
    moduleId: 0,
    methods: {
      'show': {
        methodID: 0
      }
    },
    constants: {
      'SHORT': '0',
      'LONG': '1'
    }
  },
  'moduleB': {
    moduleId: 0,
    methods: {
      'method1': {
        methodID: 0
      }
    },
    'key1': 'value1',
    'key2': 'value2'
  }
}

MessageQueue.js 构造函数中首先定义了一些实例变量,注释里面的js module指的是只在js中定义的模块,native module指的是在native(这里就是Java)层定义的模块,这些模块都可以在js中使用。

this.RemoteModules = {};//存储最终生成的各个模块信息,包含模块名,模块中的方法,常量等信息
this._require = customRequire || require;//用于加载模块的函数
this._queue = [[],[],[]];//队列,用于存放调用的模块,方法和参数信息,分别存储在第一二三个数组中
this._moduleTable = {};//moduleId查找moduleName的map,用于js module
this._methodTable = {};//methodId查找methodName的map,用于js module
this._callbacks = [];//回调函数数组,和queue一一对应,每个queue中调用的方法,如果有回调函数,那么就在这个数组的对应坐标上
this._callbackID = 0;//回调函数的id,自增

this._genModules(remoteModules);
localModules && this._genLookupTables(
  localModules, this._moduleTable, this._methodTable);

this._debugInfo = {};//放置一些debug相关的信息,主要是调用模块,函数,参数的信息
this._remoteModuleTable = {};//moduleId查找moduleName的map,用于native module
this._remoteMethodTable = {};//methodId查找methodName的map,用于native module

this._genLookupTables(
  remoteModules, this._remoteModuleTable, this._remoteMethodTable);

_genModules(remoteModules) 遍历传过来的remoteModules所有的key,得到moduleName,然后针对每个module调用_genModule方法

_genModules(remoteModules) {
  let moduleNames = Object.keys(remoteModules);
  for (var i = 0, l = moduleNames.length; i < l; i++) {
    let moduleName = moduleNames[i];
    let moduleConfig = remoteModules[moduleName];
    this.RemoteModules[moduleName] = this._genModule({}, moduleConfig);
  }
}

_genModule(module, moduleConfig) _genModule方法和_genModules方法类似,遍历module下面的所有的方法,对每个方法,调用_genMethod方法。

_genModule(module, moduleConfig) {
  let methodNames = Object.keys(moduleConfig.methods);
  for (var i = 0, l = methodNames.length; i < l; i++) {
    let methodName = methodNames[i];
    let methodConfig = moduleConfig.methods[methodName];
    module[methodName] = this._genMethod(
      moduleConfig.moduleID, methodConfig.methodID, methodConfig.type);
  }
  Object.assign(module, moduleConfig.constants);
  return module;
}
_genMethod(module, method, type) {
  ...
  fn = function(...args) {
    let lastArg = args.length > 0 ? args[args.length - 1] : null;
    let secondLastArg = args.length > 1 ? args[args.length - 2] : null;
    let hasSuccCB = typeof lastArg === 'function';
    let hasErrorCB = typeof secondLastArg === 'function';
    hasErrorCB && invariant(
      hasSuccCB,
      'Cannot have a non-function arg after a function arg.'
    );
    let numCBs = hasSuccCB + hasErrorCB;
    let onSucc = hasSuccCB ? lastArg : null;
    let onFail = hasErrorCB ? secondLastArg : null;
    args = args.slice(0, args.length - numCBs);
    return self.__nativeCall(module, method, args, onFail, onSucc);
  };
}

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

  • 方法首先检查是否有onFail和onSucc,如果有的话就压入_callbacks栈中,同时把_callbackID存入参数中。
  • 接着可以看到,_queue其实被当做了三个栈来使用,分别压入模块名,方法名和参数信息。
  • 到这里MessageQueue的构造函数就分析的差不多了,那么我们最开始的ToastAndroid.show(message, duration);方法调用,其实就是往_queue栈中压入了一些信息而已,那么最终是怎么在Java层调用到Android原生的Toast模块的呢?
__nativeCall(module, method, params, onFail, onSucc) {
  if (onFail || onSucc) {
    // eventually delete old debug info
    (this._callbackID > (1 << 5)) &&
      (this._debugInfo[this._callbackID >> 5] = null);

    this._debugInfo[this._callbackID >> 1] = [module, method];
    onFail && params.push(this._callbackID);
    this._callbacks[this._callbackID++] = onFail;
    onSucc && params.push(this._callbackID);
    this._callbacks[this._callbackID++] = onSucc;
  }
  this._queue[MODULE_IDS].push(module);
  this._queue[METHOD_IDS].push(method);
  this._queue[PARAMS].push(params);

  var now = new Date().getTime();
  if (global.nativeFlushQueueImmediate &&
      now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
    global.nativeFlushQueueImmediate(this._queue);
    this._queue = [[],[],[]];
    this._lastFlush = now;
  }

  if (__DEV__ && SPY_MODE && isFinite(module)) {
    console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
      this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
  }
}

  • 这里的关键就是__nativeCall中调用的nativeFlushQueueImmediate方法,这个方法其实C++代码中注入到Js的一个全局变量,具体怎么注入的,就是在JSCExecutor.cpp中调用installGlobalFunction,installGlobalFunction的是通过JavaScriptCore的API来实现让Js可以调用C++代码的。
  • nativeFlushQueueImmediate其实又调用了JSCExecutor.cpp中的flushQueueImmediate方法。
  • 其中,m_flushImmediateCallback是在JSCExecutor的构造函数中初始化。
  • 那么JSCExecutor对象又是在哪了被创建出来的呢?RN中是通过JSCExecutorFactory这个工厂的createJSExecutor方法来创建JSCExecutor对象的,而这个方法的实现刚好就在JSCExecutor.cpp中。

JSCExecutor.cpp

JSCExecutor::JSCExecutor(FlushImmediateCallback cb) :
    m_flushImmediateCallback(cb) {}

installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate);
installGlobalFunction(m_context, "nativeLoggingHook", nativeLoggingHook);
installGlobalFunction(m_context, "nativePerformanceNow", nativePerformanceNow);

void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
  m_flushImmediateCallback(queueJSON);
}

std::unique_ptr JSCExecutorFactory::createJSExecutor(FlushImmediateCallback cb) {
  return std::unique_ptr(new JSCExecutor(cb));
}

  • 接下来问题又来了,又是谁调用了JSCExecutorFactory.createJSExecutor呢?答案就是Bridge.cpp,
  • Bridge.cpp是RN的Jni层的入口,Java层大部分调用的Jni函数都是在这个文件中定义的。

Bridge.cpp

Bridge::Bridge(const RefPtr& jsExecutorFactory, Callback callback) :
  m_threadState.reset(new JSThreadState(jsExecutorFactory, std::move(proxyCallback)));

JSThreadState(const RefPtr& jsExecutorFactory, Bridge::Callback&& callback) :
    m_callback(callback)
  {
    m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON) {
      m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
    });
  }

ReactBridge.java回去调用jni中注册的initialize方法。RN所有jni中注册的方法,都在OnLoad.cpp。

OnLoad.cpp 可以看到initialize方法其实就是OnLoad.cpp中的bridge这个namespace下得create方法

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),
});
static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback,
                   jobject callbackQueueThread) {
  auto weakCallback = createNew(callback);
  auto weakCallbackQueueThread = createNew(callbackQueueThread);
  auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector calls, bool isEndOfBatch) {
    dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
  };
  auto nativeExecutorFactory = extractRefPtr(env, executor);
  auto bridge = createNew(nativeExecutorFactory, bridgeCallback);
  setCountableForJava(env, obj, std::move(bridge));
}

这里调用了Bridge类的构造函数,而Bridge的构造函数中,回去构造一个JSThreadState对象,Bridge所有的API调用都会委托给这个创建的JSThreadState对象。

Bridge::Bridge(const RefPtr& jsExecutorFactory, Callback callback) :
  m_callback(callback),
  m_destroyed(std::shared_ptr(new bool(false)))
{
  auto destroyed = m_destroyed;
  auto proxyCallback = [this, destroyed] (std::vector calls, bool isEndOfBatch) {
    if (*destroyed) {
      return;
    }
    m_callback(std::move(calls), isEndOfBatch);
  };
  m_threadState.reset(new JSThreadState(jsExecutorFactory, std::move(proxyCallback)));
}
  • JSThreadState的构造函数中,会去创建一个JSCExecutor类的对象
  • 这里createJSExecutor方法中,传递的是一个C++的函数,这个函数会被赋值给m_flushImmediateCallback成员变量。
JSThreadState(const RefPtr& jsExecutorFactory, Bridge::Callback&& callback) :
    m_callback(callback)
{
  m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON) {
    m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
  });
}

找到了创建JSCExecutor对象的地方了,回到刚才,我们说,所有的Js调用Native Module的API的时候,都会调用flushQueueImmediate方法,而flushQueueImmediate方法中会去调用m_flushImmediateCallback函数。

JSCExecutor::JSCExecutor(FlushImmediateCallback cb) :
    m_flushImmediateCallback(cb) {
std::unique_ptr JSCExecutorFactory::createJSExecutor(FlushImmediateCallback cb) {
  return std::unique_ptr(new JSCExecutor(cb));
}

也就是说,Js层所有的API调用,都会走到这个m_flushImmediateCallback函数的调用中,这个函数是实现Js和Native通讯的核心。而这里的m_flushImmediateCallback在CPP层,最终是在OnLoad.cpp的bridge::create方法中传入的,这个create方法又是由Java层来调用的,Bridge.java的构造函数中会调用这个create方法,所以说,Js层调用Native API的时候,最终就是调用了Bridge.java中传递过来的ReactCallback对象

public ReactBridge(
      JavaScriptExecutor jsExecutor,
      ReactCallback callback,
      MessageQueueThread nativeModulesQueueThread) {
        .....
}
  • ReactBridge对象的创建,是在CatalystInstanceImpl.java中
  • 这里传递的是ReactCallback类的子类NativeModulesReactCallback,最终调用的是call方法,而call方法又调用了mJavaRegistry.call方法。看到mJavaRegistry,开发过RN应用的同学应该感到很熟悉了,对的,这里就是对应我们在应用启动的时候注册的NativeModule对象。
private ReactBridge initializeBridge
  bridge = new ReactBridge(
            jsExecutor,
            new NativeModulesReactCallback(),
            mCatalystQueueConfiguration.getNativeModulesQueueThread());

private class NativeModulesReactCallback implements ReactCallback {
      public void call(int moduleId, int methodId, ReadableNativeArray parameters) {
        mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters);
    }
}

NativeModuleRegistry.Java

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

我们在RN中注册NativeModule是通过add一个ReactPackage对象来实现,家下来我们看一下,RN是如何把我们注册的各个module添加到NativeModuleRegistry中的。

mReactInstanceManager = ReactInstanceManager.builder()
                .addPackage(new MainReactPackage())
                .build();

ReactInstanceManager.Builder的build方法,会new一个ReactInstanceManagerImpl对象,把我们的ReactPackage对象传递过去。ReactInstanceManagerImpl类的核心就是createReactContext方法,createReactContext会首先遍历所有注册的ReactPackage,对所有的NativeModule,构造一个ModuleDefinition对象,保存到nativeModuleRegistry对象的mModuleTable中。最后,createReactContext会通过CatalystInstanceImpl.Builder构造一个CatalystInstance对象,并把包含各个NativeModule信息的nativeModuleRegistry对象传递过去。具体的模块的注册过程,我后面再写一篇单独的博客介绍,此处就不啰嗦了。

private ReactApplicationContext createReactContext(JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
    ...
    for (ReactPackage reactPackage : mPackages) {
      processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    }
    ...
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
      .setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
      .setJSExecutor(jsExecutor)
      .setRegistry(nativeModuleRegistry)
      .setJSModulesConfig(javaScriptModulesConfig)
      .setJSBundleLoader(jsBundleLoader)
      .setNativeModuleCallExceptionHandler(exceptionHandler);
}

回到我们最开始的例子中,我们要在Js中使用Toast,那么我们的MainReactPackage中,就需要构造一个ToastModule,来注册给RN,这样才可以给Js调用。

public List createNativeModules(ReactApplicationContext reactContext) {
    return Arrays.asList(
      new ToastModule(reactContext));
  }

需要注意的是,所有要给Js中使用的模块,都需要在Native这边注册一个对应的Module


在Js中调用ToastAndroid这个模块的流程就是,Js调用会调用到C++中m_flushImmediateCallback函数,参数就是Js函数的参数加上调用的模块名构成的一个JSON字符串。C++中,有一个parseMethodCalls方法,会从Js传递的JSON中,解析出moduleName,functionName,参数等一系列信息,然后C++层会调用Java层的ReactCallback类,Java代码中,会根据传递来的moduleName,functionName找到对应的模块中的方法,然后通过反射执行这些方法,并把参数传递过去。这样就完成了Js对Native代码的调用。

你可能感兴趣的:(RN中Js如何调用Native的代码)