Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处。 【React Native For Android】 Java<>Js 通信机制源码剖析
本文从源码角度剖析 RNA 中 Java <> Js 的通信机制(基于最新的 RNA Release 20)。
对于传统 Java<>Js 通信而言,Js 调用 Java 通不外乎 Jsbridge、onprompt、log 及 addjavascriptinterface 四种方式,在 Java 调用 Js 只有 loadurl 及高版本才支持的 evaluateJavaScript 两种。但在 RN 中没有采用了传统 Java 与 Js 之间的通信机制,而是借助 MessageQueue 及模块配置表,将调用转化为{moduleID, methodID,callbackID,args},处理端在模块配置表里查找注册的模块与方法并调用。
一、Module Registry
在 RNA 中,在应用启动时根据 ReactPackage 会自动生成 NativeModuleRegistry 及 JavaScriptModuleRegistry 两份模块配置表,包含系统及自定义模块,Java 端与 Js 端持有相同的模块配置表,标识为可识别为 Native 模块或 Js 模块都是通过实现相应接口,并将实例添加 ReactPackage 的 CreactXXModules 方法即可。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
CoreModulesPackage.java
@Override
public
List<NativeModule> createNativeModules(
ReactApplicationContext catalystApplicationContext) {
return
Arrays.<NativeModule>asList(
new
AndroidInfoModule(),
new
DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),
new
DebugComponentOwnershipModule(catalystApplicationContext));
}
@Override
public
List<Class<?
extends
JavaScriptModule>> createJSModules() {
return
Arrays.asList(
DeviceEventManagerModule.RCTDeviceEventEmitter.
class
,
JSTimersExecution.
class
,
RCTEventEmitter.
class
,
RCTNativeAppEventEmitter.
class
,
AppRegistry.
class
}
@Override
public
List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return
new
ArrayList<>(
0
);
}
|
Js 模块 extends 自 JavascriptModule,映射在 Js 相对应 Js 模块,通过动态代理实现调用 Js 模块。下例 AppRegistry.java 为在加载完 Jsbundle 后,Native 去启动 React Application 的总入口,appkey 为应用的 ID。映射每个 JavascriptModule 的信息保存在 JavaScriptModuleRegistration 中,统一由 JavaScriptModuleRegistry 统一管理。
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
|
AppRegistry.java
public
interface
AppRegistry
extends
JavaScriptModule {
void
runApplication(String appKey, WritableMap appParameters);
void
unmountApplicationComponentAtRootTag(
int
rootNodeTag);
}
|
Java 模块 extends 自 BaseJavaModule,在 Js 层存在同名文件识别为可调用的 Native。重写 getName 识别为 Js 的模块名,重写 getConstants 识别为 Js 可访问的常量,方法通过注解 @ReactMethod 可识别供 Js 调用的 API 接口,所有 Java 层提供的模块接口统一由 NativeModuleRegistry 统一暴露。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
AndroidInfoModule.java
public
class
AndroidInfoModule
extends
BaseJavaModule {
@Override
public
String getName() {
return
"AndroidConstants"
;
}
@Override
public
@Nullable
Map<String, Object> getConstants() {
HashMap<String, Object> constants =
new
HashMap<String, Object>();
constants.put(
"Version"
, Build.VERSION.SDK_INT);
return
constants;
}
}
|
二、Java -> Js
完整通信机制流程图:
简要说明下这5个步骤:
1.CatalystanceImpl 为 Js<>Java 通信高层封装实现类,业务模块通过 ReactInstanceManager 与 CatalystanceImpl 间接通信,调用Js暴露出来的API。
2.将来自Java层的调用拆分为 ModuleID,MethodID 及 Params,JavaScriptModuleInvocationHandler 通过动态代理方式交由 CatalystanceImpl 统一处理。
5.最终通过 JSCHelper 的 evaluateScript 的方法将 ModuleID,MethodID 及 Params 借助 JSC 传递给 Js 层。
整体调用关系比较清晰,下面分别借助源码说明上面整个流程。
在 Java 层 implements JavaScriptModule 这个 interface 被识别为 Js 层暴露的公共 Module,(由JS不允许的方法名称重载,所以继承自 JavaScriptModule 同样不允许方法重载)。JavaScriptModuleRegistry 负责管理所有的 JavaScriptModule,持有对 JavaScriptModuleInvocationHandler 的引用,通过 invoke 的方式,统一调度从 Java -> Js 的调用
。
[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
JavaScriptModuleInvocationHandler.java
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
;
}
}
|
CatalystInstance 为 Java 与 Js 之前通信的高层接口,已被抽离成接口,CatalystInstanceImpl 为其基础实现类,业务侧在 ReactInstanceManager Create ReactContext 时通过 Builder 构建实例化,业务一般不直接持有 CatalystInstance 的引用,一般通过 Framework 层的 ReactInstanceManager 的实现类进行访问。持有对 JavaScriptModuleRegistry& RativeModuleRegistry 的引用。
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
|
CatalystInstanceImpl.java
@Override
public
<T
extends
JavaScriptModule> T getJSModule(Class<T> jsInterface) {
return
Assertions.assertNotNull(mJSModuleRegistry).getJavaScriptModule(jsInterface);
}
|
在 CatalystInstance 初始化时会调用 initializeBridge 初始化私有成员 ReactBridge,ReactBridge 做为 JNI 层的通信桥接对象,负责 Java<>JCS 之间的通信。在 Java 层调用 JS 会调用 JNI 的 CallFunction 的方法,通过 JSC 转接到 JS 层的模块。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
CatalystInstanceImpl.java
private
ReactBridge initializeBridge(
JavaScriptExecutor jsExecutor,
JavaScriptModulesConfig jsModulesConfig) {
mReactQueueConfiguration.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(),
mReactQueueConfiguration.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;
}
ReactBridge.java
/**
* 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();
|
Onload.cpp 为 C++ 层主要入口,涵盖类型操作,jsbundle 加载及全局变量操作等。通过 bridge.cpp 的转接到 JSExector.cpp 执行 JS。JSExector.cpp 最终将调用转发到 JSCHelper.cpp 中执行evaluateScript 的函数,从而执行 JS 的调用。
[C++]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
OnLoad.cpp
static
void
callFunction(JNIEnv* env, jobject obj, jint moduleId, jint methodId,
NativeArray::jhybridobject args) {
auto bridge = extractRefPtr<Bridge>(env, obj);
auto arguments = cthis(wrap_alias(args));
try
{
bridge->callFunction(
(
double
) moduleId,
(
double
) methodId,
std::move(arguments->array)
);
}
catch
(...) {
translatePendingCppExceptionToJavaException();
}
Bridge.cpp
void
Bridge::callFunction(
const
double
moduleId,
const
double
methodId,
const
folly::dynamic& arguments) {
if
(*m_destroyed) {
return
;
}
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE,
"Bridge.callFunction"
);
#endif
auto returnedJSON = m_jsExecutor->callFunction(moduleId, methodId, arguments);
m_callback(parseMethodCalls(returnedJSON),
true
);
}
JSCExectutor.cpp
std::string JSCExecutor::callFunction(
const
double
moduleId,
const
double
methodId,
const
folly::dynamic& arguments) {
std::vector<folly::dynamic> call{
(
double
) moduleId,
(
double
) methodId,
std::move(arguments),
};
return
executeJSCallWithJSC(m_context,
"callFunctionReturnFlushedQueue"
, std::move(call));
}
JSCHelpers.cpp
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source,
const
char
*cachePath) {
JSValueRef exn, result;
#if WITH_FBJSCEXTENSIONS
if
(source){
result = JSEvaluateScriptWithCache(context, script, NULL, source, 0, &exn, cachePath);
}
else
{
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
}
#else
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
#endif
if
(result == nullptr) {
Value exception = Value(context, exn);
std::string exceptionText = exception.toString().str();
FBLOGE(
"Got JS Exception: %s"
, exceptionText.c_str());
auto line = exception.asObject().getProperty(
"line"
);
std::ostringstream locationInfo;
std::string file = source != nullptr ? String::adopt(source).str() :
""
;
locationInfo <<
"("
<< (file.length() ? file :
"<unknown file>"
);
if
(line != nullptr && line.isNumber()) {
locationInfo <<
":"
<< line.asInteger();
}
locationInfo <<
")"
;
throwJSExecutionException(
"%s %s"
, exceptionText.c_str(), locationInfo.str().c_str());
}
return
result;
}
|
至此,从 Java -> C++ 层调用链结束,JSC 将执行 JS 调用,在 JS Framewrok 层接收来自 C++的调用为 MessageQueue.js 的 callFunctionReturnFlushedQueue。在调用 CallFunction 执行 Js 后,会调用 flushedQueue 更新队列。
[C++]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
MessageQueue.js
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
this
.__callFunction(module, method, args);
this
.__callImmediates();
});
return
this
.flushedQueue();
}
MessageQueue.js
__callFunction(module, method, args) {
this
._lastFlush =
new
Date().getTime();
this
._eventLoopStartTime =
this
._lastFlush;
if
(isFinite(module)) {
method =
this
._methodTable[module][method];
module =
this
._moduleTable[module];
}
Systrace.beginEvent(`${module}.${method}()`);
if
(__DEV__ && SPY_MODE) {
console.
log
(
'N->JS : '
+ module +
'.'
+ method +
'('
+ JSON.stringify(args) +
')'
);
}
var moduleMethods =
this
._callableModules[module];
invariant(
!!moduleMethods,
'Module %s is not a registered callable module.'
,
module
);
moduleMethods[method].apply(moduleMethods, args);
Systrace.endEvent();
}
|
三、Js -> Java
对于 JS -> Java 调用的设计相对独特,在 React Native 的设计中, JS 是不能直接调用 Java 的接口的,而是将来自 JS 层的调用 Push 到 JS 层的一个 MessageQueue 中,在事件发生时会调用 JS 相应的模块方法去处理,处理完这些事件后再执行 JS 想让 Java 执行的方法,与 native 开发里事件响应机制是一致的。
完整通信机制流程图:
简要说明下这5个步骤:
1.JS 层调用 Java 层暴露的 API。
2.将来自 JS 层的调用拆分为 ModuleID,MethodID 及 Params 分别 push 进相应的 queue 中。
3.当事件发生时,会执行从 Java -> JS 上面这条调用链路。
4.在执行完 callFunctionReturnFlushedQueue 后,会调用 flushedQueue 并返回 MessageQueue,即刷新后的队列。
5.Java 层的 JavaRegistry 根据模块配置表调用相应模块执行。
下面分别借助源码说明上面整个流程。
[C++]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
MessageQueue.js
__nativeCall(module, method, params, onFail, onSucc) {
if
(onFail || onSucc) {
(
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;
}
global.nativeTraceBeginAsyncFlow &&
global.nativeTraceBeginAsyncFlow(TRACE_TAG_REACT_APPS,
'native'
,
this
._callID);
this
._callID++;
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
._callID];
this
._lastFlush = now;
}
Systrace.counterEvent(
'pending_js_to_native_queue'
,
this
._queue[0].length);
if
(__DEV__ && SPY_MODE && isFinite(module)) {
console.
log
(
'JS->N : '
+
this
._remoteModuleTable[module] +
'.'
+
this
._remoteMethodTable[module][method] +
'('
+ JSON.stringify(params) +
')'
);
}
}
MessageQueue.js
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
this
.__callFunction(module, method, args);
this
.__callImmediates();
});
return
this
.flushedQueue();
}
invokeCallbackAndReturnFlushedQueue(cbID, args) {
guard(() => {
this
.__invokeCallback(cbID, args);
this
.__callImmediates();
});
return
this
.flushedQueue();
}
flushedQueue() {
this
.__callImmediates();
let queue =
this
._queue;
this
._queue = [[], [], [],
this
._callID];
return
queue[0].length ? queue : null;
}
|
Js 层通过调用__nativeCall 将 ModuleID,MethodID 及 Params 放入不同队列。当 Java 层事件发生后会调用 Java -> Js 整个调用链,最终到 flushedQueue 并返回 MessageQueue。
Java 层收到来自 MessageQueue 的调用信息,查询 Java 层模块配置表,调用相应模块相应接口。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
CatalystInstanceImpl$NativeModulesReactCallback.java
private
class
NativeModulesReactCallback
implements
ReactCallback {
@Override
public
void
call(
int
moduleId,
int
methodId, ReadableNativeArray parameters) {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
if
(mDestroyed) {
return
;
}
mJavaRegistry.call(CatalystInstanceImpl.
this
, moduleId, methodId, parameters);
}
}
|
查询到相应 Java 模块,通过反射调用相应接口。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
|
BaseJavaModule.java
@Override
public
void
invoke(CatalystInstance catalystInstance, ReadableNativeArray parameters) {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"callJavaModuleMethod"
);
try
{
mMethod.invoke(BaseJavaModule.
this
, mArguments);
}
catch
(IllegalArgumentException ie) {
}
}
finally
{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
|
上述为ReactNative通信核心机制,实验数据证明该机制通信效率相对优于传统Hybrid的通信方式,同时,ReactNative中从<text />到TextView等View的映射同样也是使上该机制。
|