ReactNative: 源碼分析系列
ReactNative 啟動過程源碼分析
ReactNative 通信機制_java端源碼分析
ReactNative 通信機制_c++端源碼分析
ReactNative 通信機制_js端源碼分析
通信機制主要分成三部分,java端,c++端以及js端。調用的方式分成兩種JavaScriptModule和NativeModule。
- JavaScriptModule:表示
js
端提供的模型以及方法,java調用模型的方法既可以通知js端,典型的有AppRegistry。java端就是一個接口AppRegistry
,而js
端AppRegistry.js
提供了具體操作。 - NativeModule:表示java端提供了模型和方法,供js端來回調。
這一節我們主要分析java端的源碼:
JavaScriptModuleRegistry
通過動態代理的方式,回調CatalystInstance.callFunction方法,然后調用CatalystInstance.jniCallJSFunction方法,從而調用CatalystInstance.cpp對應jniCallJSFunction,最終調用到js端MessageQueue.js的__callFunction方法,然后就可以找到js端JavaScriptModule對應的方法并調用。
public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
CatalystInstance instance,
Class<T> moduleInterface) {
JavaScriptModule module = mModuleInstances.get(moduleInterface);
if (module != null) {
return (T) module;
}
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
moduleInterface.getClassLoader(),
new Class[]{moduleInterface},
new JavaScriptModuleInvocationHandler(instance, moduleInterface));
mModuleInstances.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
}
通過JavaScriptModule的class來獲取它的代理實例。
我們并不需要JavaScriptModule的真正的實例,因為它的實現是在js端,java端只需要提供對應的module模板名,method方法名以及args調用參數,就可以回調js端對應的方法了。
JavaScriptModuleInvocationHandler
@Override
public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
NativeArray jsArgs = args != null
? Arguments.fromJavaArgs(args)
: new WritableNativeArray();
mCatalystInstance.callFunction(getJSModuleName(), method.getName(), jsArgs);
return null;
}
當我們用得到的代理實例JavaScriptModule調用方法時,就會回調這個方法。
例如catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);就會調用這個方法。
這個方法會調用mCatalystInstance.callFunction方法,提供JavaScriptModule的module、method、args。從而回調jniCallJSFunction方法,最終會調用到js端JavaScriptModule對應方法。
NativeModuleRegistry
向c++端提供java端所有的NativeModule。主要是兩個方法:getJavaModules和getCxxModules。這個兩個方法都是c++端直接調用的。
/* package */ Collection<JavaModuleWrapper> getJavaModules(
JSInstance jsInstance) {
ArrayList<JavaModuleWrapper> javaModules = new ArrayList<>();
for (Map.Entry<Class<? extends NativeModule>, ModuleHolder> entry :
mModules.entrySet()) {
Class<? extends NativeModule> type = entry.getKey();
if (!CxxModuleWrapperBase.class.isAssignableFrom(type)) {
javaModules.add(new JavaModuleWrapper(jsInstance, type, entry.getValue()));
}
}
return javaModules;
}
返回一個JavaModuleWrapper的集合。
接受一個參數JSInstance,它是一個接口,只有一個invokeCallback方法,作用是向js端傳遞數據。考慮下面情況,我們在js端調用NativeModule的方法,如果這是一個異步方法,那么這個方法的結果值怎么回傳給js端呢,就是通過這個JSInstance(注意如果是同步方法就不用了,因為同步方法結果值是直接返回的)。
遍歷所有的mModules,排除所有CxxModuleWrapperBase子類的NativeModule,然后創建JavaModuleWrapper實例添加到集合中。
JavaModuleWrapper
主要是向c++端這個NativeModule所有的方法說明,Constants常量,以及接受c++回調。
@DoNotStrip
public List<MethodDescriptor> getMethodDescriptors() {
if (mDescs.isEmpty()) {
findMethods();
}
return mDescs;
}
這個方法是c++端調用的,返回這個NativeModule的所有被@ReactMethod注釋方法的描述。
mDescs和mMethods這兩個集合大小相同,儲存的方法也是一一對應的,mDescs返回給c++端,所以c++就可以集合下標索引找到mMethods對應的方法,就可以進行回調了。
@DoNotStrip
private void findMethods() {
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "findMethods");
Set<String> methodNames = new HashSet<>();
Class<? extends NativeModule> classForMethods = mModuleClass;
Class<? extends NativeModule> superClass =
(Class<? extends NativeModule>) mModuleClass.getSuperclass();
if (ReactModuleWithSpec.class.isAssignableFrom(superClass)) {
// For java module that is based on generated flow-type spec, inspect the
// spec abstract class instead, which is the super class of the given java
// module.
classForMethods = superClass;
}
Method[] targetMethods = classForMethods.getDeclaredMethods();
for (Method targetMethod : targetMethods) {
ReactMethod annotation = targetMethod.getAnnotation(ReactMethod.class);
if (annotation != null) {
String methodName = targetMethod.getName();
if (methodNames.contains(methodName)) {
// We do not support method overloading since js sees a function as an object regardless
// of number of params.
throw new IllegalArgumentException(
"Java Module " + getName() + " method name already registered: " + methodName);
}
MethodDescriptor md = new MethodDescriptor();
JavaMethodWrapper method = new JavaMethodWrapper(this, targetMethod, annotation.isBlockingSynchronousMethod());
md.name = methodName;
md.type = method.getType();
if (md.type == BaseJavaModule.METHOD_TYPE_SYNC) {
md.signature = method.getSignature();
md.method = targetMethod;
}
mMethods.add(method);
mDescs.add(md);
}
}
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
這個方法通過反射,得到NativeModule所有被@ReactMethod注解的方法,創建MethodDescriptor和JavaMethodWrapper存入對應的列表中。
注意當ReactMethod方法是同步方法時,MethodDescriptor會儲存這個method和signature,因為當是同步方法時,js端要直接獲取java端方法調用的結果值,所以就不能通過invoke方法回調了。c++端擁有方法的引用(就是這個MethodDescriptor的method),直接調用得到結果值,返回給js端。
仔細看這段代碼,會發現有個問題。就是methodNames這個集合,它的作用是判斷不允許有方法名重復的方法,可惜這個集合從來沒有被添加過數據,所以就沒有作用。那么為什么不能方法名重復呢,我們都知道java中方法名重復參數不一樣,叫做方法重載,但是js是沒有方法重載的。所以我們在NativeModule中定義重載方法,js端就只有一個方法,就會出現問題。
@DoNotStrip
public @Nullable NativeMap getConstants() {
if (!mModuleHolder.getHasConstants()) {
return null;
}
final String moduleName = getName();
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE,
"JavaModuleWrapper.getConstants")
.arg("moduleName", moduleName)
.flush();
ReactMarker.logMarker(GET_CONSTANTS_START, moduleName);
BaseJavaModule baseJavaModule = getModule();
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "module.getConstants");
Map<String, Object> map = baseJavaModule.getConstants();
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "create WritableNativeMap");
ReactMarker.logMarker(CONVERT_CONSTANTS_START, moduleName);
try {
return Arguments.makeNativeMap(map);
} finally {
ReactMarker.logMarker(CONVERT_CONSTANTS_END);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(GET_CONSTANTS_END);
SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
}
}
這個方法也是由c++端調用,通過調用getConstants()得到NativeModule的常量,供給js端使用。
@DoNotStrip
public void invoke(int methodId, ReadableNativeArray parameters) {
if (mMethods == null || methodId >= mMethods.size()) {
return;
}
mMethods.get(methodId).invoke(mJSInstance, parameters);
}
也是由c++端調用,來調用NativeModule對應的異步方法(同步方法在c++直接調用,不會走這里)。
methodId表示在集合中的索引(c++和java端方法列表的索引是一一對應的),parameters:js端傳遞來的參數。mJSInstance:用于將方法的結果值通知給js端。
JavaMethodWrapper
調用NativeModule對應的方法,但是要處理兩個問題,第一處理ReadableNativeArray數據將它轉成方法對應的參數,第二區分處理NativeModule方法對應的三種類型sync、async、 promise。
- sync表示同步方法,js端直接得到java端方法調用的返回值。
- async和promise都是異步方法,它們本質上都是通過Callback.java這個類的invoke方法,回調JSInstance的invokeCallback方法,調用子類CatalystInstanceImpl的invokeCallback方法,從而調用c++的jniCallJSCallback方法,最終調用js端MessageQueue.js的__invokeCallback方法,將java端方法調用結果值返回給js端。只不過promise方式對Callback.java進行了包裝(具體參考PromiseImpl.java)
private static abstract class ArgumentExtractor<T> {
public int getJSArgumentsNeeded() {
return 1;
}
public abstract @Nullable T extractArgument(
JSInstance jsInstance, ReadableNativeArray jsArguments, int atIndex);
}
它是用來提取ReadableNativeArray中的參數。
static final private ArgumentExtractor<Callback> ARGUMENT_EXTRACTOR_CALLBACK =
new ArgumentExtractor<Callback>() {
@Override
public @Nullable Callback extractArgument(
JSInstance jsInstance, ReadableNativeArray jsArguments, int atIndex) {
if (jsArguments.isNull(atIndex)) {
return null;
} else {
int id = (int) jsArguments.getDouble(atIndex);
return new com.facebook.react.bridge.CallbackImpl(jsInstance, id);
}
}
};
id:這個參數js端使用,來確定返回值給對應module的調用。CallbackImpl這個類主要會調用invokeCallback方法,最終會調用到MessageQueue.js的__invokeCallback方法。
static final private ArgumentExtractor<Promise> ARGUMENT_EXTRACTOR_PROMISE =
new ArgumentExtractor<Promise>() {
@Override
public int getJSArgumentsNeeded() {
return 2;
}
@Override
public Promise extractArgument(
JSInstance jsInstance, ReadableNativeArray jsArguments, int atIndex) {
Callback resolve = ARGUMENT_EXTRACTOR_CALLBACK
.extractArgument(jsInstance, jsArguments, atIndex);
Callback reject = ARGUMENT_EXTRACTOR_CALLBACK
.extractArgument(jsInstance, jsArguments, atIndex + 1);
return new PromiseImpl(resolve, reject);
}
};
它需要消耗的參數是兩個,因為有正確的回調和異常的回調,返回一個PromiseImpl實例,它是Promise子類,當我們調用resolve或者reject方法時,都會調用CallbackImpl的invokeCallback,來通知js端。
private void processArguments() {
if (mArgumentsProcessed) {
return;
}
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "processArguments")
.arg("method", mModuleWrapper.getName() + "." + mMethod.getName())
.flush();
try {
mArgumentsProcessed = true;
mArgumentExtractors = buildArgumentExtractors(mParameterTypes);
mSignature = buildSignature(
mMethod,
mParameterTypes,
(mType.equals(BaseJavaModule.METHOD_TYPE_SYNC)));
mArguments = new Object[mParameterTypes.length];
mJSArgumentsNeeded = calculateJSArgumentsNeeded();
} finally {
SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
}
}
通過反射,獲取方法的參數類型列表,來生成mArgumentExtractors列表,用來解析ReadableNativeArray。
@Override
@Override
public void invoke(JSInstance jsInstance, ReadableNativeArray parameters) {
String traceName = mModuleWrapper.getName() + "." + mMethod.getName();
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE,
"callJavaModuleMethod")
.arg("method", traceName)
.flush();
if (DEBUG) {
PrinterHolder.getPrinter()
.logMessage(
ReactDebugOverlayTags.BRIDGE_CALLS,
"JS->Java: %s.%s()",
mModuleWrapper.getName(),
mMethod.getName());
}
try {
if (!mArgumentsProcessed) {
processArguments();
}
if (mArguments == null || mArgumentExtractors == null) {
throw new Error("processArguments failed");
}
if (mJSArgumentsNeeded != parameters.size()) {
throw new NativeArgumentsParseException(
traceName + " got " + parameters.size()
+ " arguments, expected " + mJSArgumentsNeeded);
}
int i = 0, jsArgumentsConsumed = 0;
try {
for (; i < mArgumentExtractors.length; i++) {
mArguments[i] = mArgumentExtractors[i].extractArgument(
jsInstance, parameters, jsArgumentsConsumed);
jsArgumentsConsumed += mArgumentExtractors[i].getJSArgumentsNeeded();
}
} catch (UnexpectedNativeTypeException e) {
throw new NativeArgumentsParseException(
e.getMessage() + " (constructing arguments for " + traceName
+ " at argument index " +
getAffectedRange(jsArgumentsConsumed,
mArgumentExtractors[i].getJSArgumentsNeeded()) +
")",
e);
}
try {
mMethod.invoke(mModuleWrapper.getModule(), mArguments);
} catch (IllegalArgumentException ie) {
throw new RuntimeException("Could not invoke " + traceName, ie);
} catch (IllegalAccessException iae) {
throw new RuntimeException("Could not invoke " + traceName, iae);
} catch (InvocationTargetException ite) {
if (ite.getCause() instanceof RuntimeException) {
throw (RuntimeException) ite.getCause();
}
throw new RuntimeException("Could not invoke " + traceName, ite);
}
} finally {
SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
}
}
將parameters轉換成method對應的參數,然后通過 mMethod.invoke(mModuleWrapper.getModule(), mArguments)進行方法調用。
CatalystInstanceImpl
通過這個類主動調用c++端方法
private native void initializeBridge(
ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
MessageQueueThread uiBackgroundQueue,
Collection<JavaModuleWrapper> javaModules,
Collection<ModuleHolder> cxxModules);
調用CatalystInstanceImpl.cpp中對應方法。
- ReactCallback:用于c++回調。
- jsExecutor:js執行器,對應JSCExecutor.cpp實例。
- jsQueue、moduleQueue、uiBackgroundQueue用c++不同線程中的回調。
- javaModules、cxxModules將NativeModule傳遞給C++。
private native void jniCallJSFunction(
String module,
String method,
NativeArray arguments);
這個方法用來調用js端JavaScriptModule對應方法。
當在java端調用JavaScriptModule方法時,
例如catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams))
就會調用到JavaScriptModuleRegistry中JavaScriptModuleInvocationHandler內部類的invoke方法,調用CatalystInstanceImpl的callFunction方法,接著調用PendingJSCall的call方法,就會調用到jniCallJSFunction這個方法了。
private native void jniCallJSCallback(int callbackID, NativeArray arguments);
當js端調用NativeModule的異步方法時,通過這個方法返回java端方法調用的結果值的。
java端NativeModule對應方法調用完成后,我們手動調用Callback的invoke方法
這個Callback是我們自定義方法形參,如果沒有定義它或者Promise,那么就表示不需要返回給js端數據,因為沒辦法將js端傳遞值。這里指的是異步方法.。
進而調用JSInstance的invokeCallback方法,然后就會回調到jniCallJSCallback這個方法。
總結
JavaScriptModule:
java端使用module名,method名通過jniCallJSFunction( String module, String method, NativeArray arguments)方法調用js端JavaScriptModule對應的方法。
注意:這種方式不能得到js端方法調用的返回值的。其實從c++和js端源碼看出它是有一個可以得到返回值的方法,可惜java端并沒有此方法的調用,具體會在接下來兩章中分析。
NativeModule:
js端調用java端方法的方式,對應js端NativeModules.js。分為同步方法和異步方法:
- 同步方法會在c++端直接調用,得到結果值,返回給js端。
- 異步方法由c++端調用JavaModuleWrapper的invoke方法,再調用JavaMethodWrapper的invoke方法,最終會調用到CatalystInstanceImpl的jniCallJSCallback方法,最后會調用到MessageQueue.js的__invokeCallback方法。
下節預覽
java端通信模塊的代碼已經分析完了,下面是c++端通信模塊的代碼。