ReactNative Android 运行原理之2021-02-06 本文已影响0人
耗子_wo
这一期我们来介绍下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的原因,而我们刚刚看到的 JSContextGetGlobalObject 与 JSObjectSetProperty 就是两个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 运行原理之<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的原因,而我们刚刚看到的 JSContextGetGlobalObject 与 JSObjectSetProperty 就是两个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>,欢迎敬请期待···