博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【腾讯Bugly干货分享】深入源码探索 ReactNative 通信机制
阅读量:5768 次
发布时间:2019-06-18

本文共 13491 字,大约阅读时间需要 44 分钟。

  hot3.png

本文从源码角度剖析 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 方法即可。

CoreModulesPackage.java@Overridepublic List
 createNativeModules(   ReactApplicationContext catalystApplicationContext) { return Arrays.
asList(   new AndroidInfoModule(),   new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),   new DebugComponentOwnershipModule(catalystApplicationContext));}@Overridepublic List
> createJSModules() {return Arrays.asList(  DeviceEventManagerModule.RCTDeviceEventEmitter.class,  JSTimersExecution.class,  RCTEventEmitter.class,  RCTNativeAppEventEmitter.class,  AppRegistry.class}@Overridepublic List
 createViewManagers(ReactApplicationContext reactContext) {  return new ArrayList<>(0);}

Js 模块 extends 自 JavascriptModule,映射在 Js 相对应 Js 模块,通过动态代理实现调用 Js 模块。下例 AppRegistry.java 为在加载完 Jsbundle 后,Native 去启动 React Application 的总入口,appkey 为应用的 ID。映射每个 JavascriptModule 的信息保存在 JavaScriptModuleRegistration 中,统一由 JavaScriptModuleRegistry 统一管理。

AppRegistry.javapublic 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 统一暴露。

AndroidInfoModule.javapublic class AndroidInfoModule extends BaseJavaModule { @Override public String getName() {  return "AndroidConstants"; } @Override public @Nullable Map
 getConstants() {   HashMap
 constants = new HashMap
();   constants.put("Version", Build.VERSION.SDK_INT);   return constants;  }}

二、Java -> Js

完整通信机制流程图:

bugly 干货分享

简要说明下这5个步骤:

1.CatalystanceImpl 为 Js<>Java 通信高层封装实现类,业务模块通过 ReactInstanceManager 与 CatalystanceImpl 间接通信,调用Js暴露出来的API。

2.将来自Java层的调用拆分为 ModuleID,MethodID 及 Params,JavaScriptModuleInvocationHandler 通过动态代理方式交由 CatalystanceImpl 统一处理。

  1. CatalystanceImpl 进一步将 ModuleID,MethodID 及 Params 转交给 ReactBridge JNI 处理。

  2. ReactBridge 调用 C++层的调用链转发 ModuleID,MethodID 及 Params。

    5.最终通过 JSCHelper 的 evaluateScript 的方法将 ModuleID,MethodID 及 Params 借助 JSC 传递给 Js 层。

整体调用关系比较清晰,下面分别借助源码说明上面整个流程。

在 Java 层 implements JavaScriptModule 这个 interface 被识别为 Js 层暴露的公共 Module,(由JS不允许的方法名称重载,所以继承自 JavaScriptModule 同样不允许方法重载)。JavaScriptModuleRegistry 负责管理所有的 JavaScriptModule,持有对 JavaScriptModuleInvocationHandler 的引用,通过 invoke 的方式,统一调度从 Java -> Js 的调用。

JavaScriptModuleInvocationHandler.javaprivate 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 的引用。

CatalystInstanceImpl.java@Overridepublic 
 T getJSModule(Class
 jsInterface) {return Assertions.assertNotNull(mJSModuleRegistry).getJavaScriptModule(jsInterface);}

在 CatalystInstance 初始化时会调用 initializeBridge 初始化私有成员 ReactBridge,ReactBridge 做为 JNI 层的通信桥接对象,负责 Java<>JCS 之间的通信。在 Java 层调用 JS 会调用 JNI 的 CallFunction 的方法,通过 JSC 转接到 JS 层的模块。

CatalystInstanceImpl.javaprivate 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 的调用。

OnLoad.cppstatic void callFunction(JNIEnv* env, jobject obj, jint moduleId, jint methodId,                     NativeArray::jhybridobject args) {  auto bridge = extractRefPtr
(env, obj);  auto arguments = cthis(wrap_alias(args));  try {   bridge->callFunction(    (double) moduleId,    (double) methodId,    std::move(arguments->array)   ); } catch (...) {   translatePendingCppExceptionToJavaException(); }Bridge.cppvoid 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 /* = isEndOfBatch */);}JSCExectutor.cppstd::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
 call{   (double) moduleId,   (double) methodId,std::move(arguments), }; return executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));}JSCHelpers.cppJSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source, const char *cachePath) {   JSValueRef exn, result;#if WITH_FBJSCEXTENSIONSif (source){   // If evaluating an application script, send it through `JSEvaluateScriptWithCache()`   //  to add cache support.  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 : "
");    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 更新队列。

MessageQueue.jscallFunctionReturnFlushedQueue(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 开发里事件响应机制是一致的。

完整通信机制流程图:

Bugly 干货分享

简要说明下这5个步骤:

1.JS 层调用 Java 层暴露的 API。

2.将来自 JS 层的调用拆分为 ModuleID,MethodID 及 Params 分别 push 进相应的 queue 中。

3.当事件发生时,会执行从 Java -> JS 上面这条调用链路。

4.在执行完 callFunctionReturnFlushedQueue 后,会调用 flushedQueue 并返回 MessageQueue,即刷新后的队列。

5.Java 层的 JavaRegistry 根据模块配置表调用相应模块执行。

下面分别借助源码说明上面整个流程。

MessageQueue.js__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; } 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.jscallFunctionReturnFlushedQueue(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 层模块配置表,调用相应模块相应接口。

CatalystInstanceImpl$NativeModulesReactCallback.javaprivate class NativeModulesReactCallback implements ReactCallback {  @Override  public void call(int moduleId, int methodId, ReadableNativeArray parameters) {   mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();  // Suppress any callbacks if destroyed - will only lead to sadness.  if (mDestroyed) {    return;  }  mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters);  }}

查询到相应 Java 模块,通过反射调用相应接口。

   BaseJavaModule.java

@Overridepublic 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); }}

更多精彩内容欢迎关注,腾讯Bugly微信公众账号

                                                             161945_ipXs_2510794.jpg

是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

转载于:https://my.oschina.net/bugly/blog/635538

你可能感兴趣的文章
cocos2d-x 3.0 rapidJson 解析操作应该注意的细节
查看>>
oracle 索引的(创建、简介、技巧、怎样查看)
查看>>
用脚本定时监控SQL Server主从一致性
查看>>
类模版的static成员
查看>>
LeetCode - Valid Parentheses
查看>>
ANDROID对文件的操作
查看>>
用互联网思维来开发客户端软件——项目开发小结
查看>>
CSS中脱离文档流是什么意思?
查看>>
ExtJS智能提示工具spket安装与破解
查看>>
[LeetCode] Regular Expression Matching 正则表达式匹配
查看>>
Shader的语法
查看>>
PowerDesigner导出表到word
查看>>
[AngularJS] angular-formly: expressionProperties
查看>>
Android实现视频录制
查看>>
机器学习算法中如何选取超参数:学习速率、正则项系数、minibatch size
查看>>
解决ftp连接出现 无法从控制 Socket 读取。Socket 错误 = #10054。
查看>>
安卓之页面跳转与传值和按钮事件
查看>>
java实现代理domino web邮件下载
查看>>
无法识别的属性“targetFramework”。请注意,属性名是大写和小写。错误的解决方案...
查看>>
Spring学习笔记之 Spring IOC容器(二) 之注入参数值,自动组件扫描方式,控制Bean实例化方式,使用注解方式...
查看>>