大神在源碼中——react-native android的通訊機(jī)制及實(shí)現(xiàn)

可能某個(gè)大神說過源碼自有黃金屋,源碼自有顏如玉。對(duì)于react-native這樣優(yōu)秀的框架,不看看他的源碼實(shí)在是有點(diǎn)可惜,本文通過對(duì)react-native 與 java 通信部分的源碼閱讀探究react-native android的通訊機(jī)制及實(shí)現(xiàn)。
<h1>總體模型</h1>


對(duì)于傳統(tǒng) Java Js 通信而言,Js 調(diào)用 Java 通不外乎 Jsbridge、onprompt、log 及 addjavascriptinterface 四種方式。但在 RN 中沒有采用了傳統(tǒng) Java 與 Js 之間的通信機(jī)制。而是自己弄一套最新的WebKit作為React-Native的解釋器,這樣做有兩個(gè)重要好處就是兼容絕大多少設(shè)備版本和方便添加自定義功能。其總體模型如上圖

<h1>java層</h1>
先上一張圖

這是從源碼中提取的java層實(shí)現(xiàn)中關(guān)鍵的類,我們一個(gè)一個(gè)的來看
<h5>JavaScriptModule</h5>
在reactAndroid下的com.facebook.react.bridge包中我們可以看到JavaScriptModule的代碼

/**
 * Interface denoting that a class is the interface to a module with * 
 * the same name in JS. Calling
 * functions on this interface will result in corresponding methods in * 
 * JS being called.
 *
 * When extending JavaScriptModule and registering it with a 
 * CatalystInstance, all public methods
 * are assumed to be implemented on a JS module with the same * 
 * name as this class. Calling methods
 * on the object returned from {@link ReactContext#getJSModule} 
 * or
 * {@link CatalystInstance#getJSModule} will result in the methods 
 * with those names exported by
 * that module being called in JS.
 *
 * NB: JavaScriptModule does not allow method name overloading 
 * because JS does not allow method name
 * overloading.
 */
@DoNotStrip
public interface JavaScriptModule {
}


</br>
作為RN各組件的接口其描述在源碼中寫得有:
1.這個(gè)接口在js層中具有同名模塊,調(diào)用這個(gè)接口會(huì)調(diào)用js層相應(yīng)的模塊
2.所有組件必須繼承自該JavaScriptModule接口,并且注冊(cè)到CatalystInstance中
3.所有public方法都應(yīng)當(dāng)在js層同名組件中被實(shí)現(xiàn)
4.其方法不允許重載,因?yàn)閖s不允許方法重載

好了,現(xiàn)在我們知道了JavaScriptModule是在js層有同名組件的接口,沒有具體實(shí)現(xiàn)類。接下來就需要看一下源碼中所說的注冊(cè)到CatalystInstance中這一過程是怎么回事了。
<h5>JavaScriptModule 和 NativeModule的注冊(cè)過程</h5>
facebook官方定義了許多組件都組裝在<b>CoreMoudulesPackage</b>(圖中上方中間)中,這里用戶可以自定義組件被組裝在mPackages中。在源碼中尋找它們被加載到何處,就能揭開注冊(cè)的過程了吧.

</br>
我們知道RNA的啟動(dòng)是這樣的:ReactActivity的委托類ReactActivityDelegate創(chuàng)建創(chuàng)建ReactRootView作為應(yīng)用的容器->調(diào)用ReactRootView.startReactApplication()進(jìn)一步執(zhí)行應(yīng)用啟動(dòng)流程

 public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties) {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication");
    try {
      UiThreadUtil.assertOnUiThread();

    ...
      Assertions.assertCondition(
        mReactInstanceManager == null,
        "This root view has already been attached to a catalyst instance manager");

      mReactInstanceManager = reactInstanceManager;
      mJSModuleName = moduleName;
      mAppProperties = initialProperties;

      if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
        mReactInstanceManager.createReactContextInBackground();
      }

      attachToReactInstanceManager();
    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }


</br>
這里創(chuàng)建了一個(gè)<b>ReactInstanceManager</b>,這是一個(gè)非常重要的實(shí)例,從名字上看,它管理著整個(gè)react實(shí)例。這里會(huì)調(diào)用createReactContextInBackground()來創(chuàng)建RN應(yīng)用的上下文。這個(gè)方法最終開啟異步任務(wù)ReactInstanceManager.createReactContext(),真正開始創(chuàng)建ReactContext。這個(gè)方法之后還會(huì)提到這里只截取我們感興趣的部分

private ReactApplicationContext createReactContext(
    JavaScriptExecutor jsExecutor,
    JSBundleLoader jsBundleLoader) {
...
  NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
  JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();
...
try {
    CoreModulesPackage coreModulesPackage =
        new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
    processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
  } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
  }

  for (ReactPackage reactPackage : mPackages) {
    Systrace.beginSection(
        Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
        "createAndProcessCustomReactPackage");
    try {
      processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }
...


</br>
好了這下看到<b>CoreMoudulesPackage</b>的去向了,說明方向?qū)α恕_@個(gè)這些截取部分具體做了這些事情:
1.創(chuàng)建<b> NativeModuleRegistry</b>和<b> JavaScriptModuleRegistry</b>的builder(建造者模式)
2.使用processPackage方法處理<b>CoreModulesPackage</b>
那么processPackage方法是怎么樣的呢

private void processPackage(
    ReactPackage reactPackage,
    ReactApplicationContext reactContext,
    NativeModuleRegistry.Builder nativeRegistryBuilder,
    JavaScriptModulesConfig.Builder jsModulesBuilder) {
  for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
    nativeRegistryBuilder.add(nativeModule);
  }
  for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
    jsModulesBuilder.add(jsModuleClass);
  }
}


</br>
這下明白了,循環(huán)處理我們?cè)贏pplication里注入的<b>CoreMoudulesPackage</b>和其他package,處理的過程就是把各自的Module添加到對(duì)應(yīng)的builder中。然后在ReactInstanceManager.createReactContext()中

try {
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}


</br>
生成Java Module注冊(cè)表<b> NativeModuleRegistry </b>
誒,那么JS Module的注冊(cè)表呢,還有源碼里說的所有javaScriptModule注冊(cè)到CatalystInstance中是怎么回事,我們接著往下看

CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
reactContext.initializeWithInstance(catalystInstance);
catalystInstance.runJSBundle();


</br>
在這段代碼中createReactContext()方法通過builder模式去創(chuàng)建一個(gè)<b>CatalystInstance</b>,并且注意.setJSModuleRegistry(jsModulesBuilder.build()),這里生成了JS Module的注冊(cè)表<b> JavaScriptModuleRegistry </b>。果然像源碼注釋說的一樣所有javaScriptModule注冊(cè)到CatalystInstance中。
至此,我們生成好了 NativeModuleRegistry 及 JavaScriptModuleRegistry 兩份模塊配置表,可是還有一個(gè)問題,這兩份注冊(cè)表是存在于java端的,那要怎么傳輸?shù)絁S端呢?
答案是將所有JavaScriptModule的信息生成JSON字符串(moduleID+methodID+ arguments)預(yù)先保存到<b>ReactBridge</b>中,為什么這么做呢,這與接下來的調(diào)用過程有關(guān)。至此java層的組件的注冊(cè)完成。

</br>
<h5>JavaScriptModule 的調(diào)用過程</h5>
前面在組件的注冊(cè)過程中描述了關(guān)鍵的類和方法,再回憶一下開頭的圖

調(diào)用是怎么來完成的呢?
在注冊(cè)過程的ReactInstanceManager.createReactContext()時(shí)有這么一段代碼

if (mCurrentReactContext != null) {
      mCurrentReactContext
          .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
 ...
    }

字面意思很好理解,在當(dāng)前react上下文中獲取JSModule,com.facebook.react.bridge 包中有ReactContext類,來看看getJSModule這個(gè)方法

public class ReactContext extends ContextWrapper {
...
/**
   * @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) {
      throw new RuntimeException(EARLY_JS_ACCESS_EXCEPTION_MESSAGE);
    }
    return mCatalystInstance.getJSModule(jsInterface);
  }
...
}

原來調(diào)用了<b>CatalystInstance</b>實(shí)例的getJSModule()方法,那就來看看這個(gè)方法

 @Override
  public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
    return mJSModuleRegistry.getJavaScriptModule(this, jsInterface);
  }

又調(diào)用了<b> JavaScriptModuleRegistry </b>的getJavaScriptModule()方法,

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;
  }

先看上半部分代碼,顯然,實(shí)例中緩存得有得就返回相應(yīng)的JSModule,但是沒有呢?
沒有肯定要去創(chuàng)建,可我們之前描述道javaScriptModule組件都是接口定義,在Java端是沒有實(shí)現(xiàn)類的,被注冊(cè)的都是Class類,沒有真正的實(shí)例,Java端又如何來調(diào)用呢,代碼中給出了答案——?jiǎng)討B(tài)代理,既然是動(dòng)態(tài)代理,那么委托類,中介類和代理類都是什么呢?
我們來看看<b> JavaScriptModuleInvocationHandler </b>類,它是<b> JavaScriptModuleRegistry </b>的內(nèi)部類

 private static class JavaScriptModuleInvocationHandler implements InvocationHandler {
    private final CatalystInstance mCatalystInstance;
    private final Class<? extends JavaScriptModule> mModuleInterface;
    private @Nullable String mName;

    public JavaScriptModuleInvocationHandler(
        CatalystInstance catalystInstance,
        Class<? extends JavaScriptModule> moduleInterface) {
      mCatalystInstance = catalystInstance;
      mModuleInterface = moduleInterface;

      if (ReactBuildConfig.DEBUG) {
        Set<String> methodNames = new HashSet<>();
        for (Method method : mModuleInterface.getDeclaredMethods()) {
          if (!methodNames.add(method.getName())) {
            throw new AssertionError(
              "Method overloading is unsupported: " + mModuleInterface.getName() +
                "#" + method.getName());
          }
        }
      }
    }

    private String getJSModuleName() {
      if (mName == null) {
        // With proguard obfuscation turned on, proguard apparently (poorly) emulates inner
        // classes or something because Class#getSimpleName() no longer strips the outer
        // class name. We manually strip it here if necessary.
        String name = mModuleInterface.getSimpleName();
        int dollarSignIndex = name.lastIndexOf('$');
        if (dollarSignIndex != -1) {
          name = name.substring(dollarSignIndex + 1);
        }

        // getting the class name every call is expensive, so cache it
        mName = name;
      }
      return mName;
    }

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

看了invoke方法后,我們就明白了,調(diào)用了代理類<b>CatalystInstance</b>的callFunction()方法,看一下這個(gè)方法

 @Override
  public void callFunction(
      final String module,
      final String method,
      final NativeArray arguments) {
    if (mDestroyed) {
      final String call = module + "." + method + "(" + arguments.toString() + ")";
      FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed: " + call);
      return;
    }
    if (!mAcceptCalls) {
      // Most of the time the instance is initialized and we don't need to acquire the lock
      synchronized (mJSCallsPendingInitLock) {
        if (!mAcceptCalls) {
          mJSCallsPendingInit.add(new PendingJSCall(module, method, arguments));
          return;
        }
      }
    }

    jniCallJSFunction(module, method, arguments);
  }


</br>
這個(gè)方法最后,在 Java 層調(diào)用 JS 會(huì)調(diào)用 JNI 的 CallFunction 的方法。具體作用就是將想用調(diào)用的方法對(duì)應(yīng)的moduleId,methodId和arguments通過jni傳遞到j(luò)s端進(jìn)行調(diào)用。看到這三個(gè)參數(shù)有沒有回想到我們?cè)谧?cè)過程最后描述的,所有JavaScriptModule信息都被以(moduleID+methodID+ arguments)形式生成的JSON字符串預(yù)先存入了ReactBridge中.就是為了調(diào)用時(shí)作為一個(gè)索引表。
<h1>bridge層</h1>
接著java層的圖示,再上一張圖


前面說到Java 層調(diào)用 JS 會(huì)調(diào)用 JNI 的 CallFunction 的方法我們來看一下

public class ReactBridge extends Countable {
  static {
    SoLoader.loadLibrary(REACT_NATIVE_LIB);
  }

   public native void callFunction(int moduleId, int methodId, NativeArray arguments);

  public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
}

如上所說是JNI調(diào)用,其接口為react/jni中的<b>OnLoad.cpp</b>,里面涵蓋了類型操作,jsbundle 加載及全局變量操作等。看一下它的代碼

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();
 }

又調(diào)用了<b>Bridge</b>,實(shí)際上是繼承自它的<b>CountableBridge</b>,再看其代碼中的callFunction()

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 /* = isEndOfBatch */);
}

回調(diào)轉(zhuǎn)接到<b>JSExector.cpp </b>,來執(zhí)行js,看看它的代碼

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

這里還有一個(gè)地方需要注意前面Java層構(gòu)造的JavaScriptModule信息JSON串在這里得到處理,作為映射表供callFunction()調(diào)用

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


</br>
最后將調(diào)用轉(zhuǎn)發(fā)到 <b>JSCHelper.cpp</b> 中執(zhí)行evaluateScript 的函數(shù),從而執(zhí)行 JS 的調(diào)用。<b>JSCHelpers</b>對(duì)WebKit的一些API做了封裝,負(fù)責(zé)最終調(diào)用WebKit.

td::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));
}
JSCHelpers.cpp

JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source, const char *cachePath) {
   JSValueRef exn, result;
#if WITH_FBJSCEXTENSIONS
if (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 : "<unknown file>");
    if (line != nullptr && line.isNumber()) {
    locationInfo << ":" << line.asInteger();
   }
   locationInfo << ")";
    throwJSExecutionException("%s %s", exceptionText.c_str(), locationInfo.str().c_str());
  }
  return result;
}

這樣java層到bridge層的調(diào)用就完成了,接下來就開始Bridge向Javascript的調(diào)用。
<h1>js層</h1>
與前面兩層相同,先上圖

在react-native\Libraries\BatchedBridge中有<b> BatchedBridge .js</b>和<b>MessageQueue.js</b>作為bridge層調(diào)用傳遞的接口,
BatchedBridge的代碼是這樣

const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue();

只是簡單的new了一個(gè)<b>MessageQueue</b>對(duì)象,看來<b>MessageQueue</b>才是真的接口,果然在這里我們看到了__callFunction()方法

__callFunction(module: string, method: string, args: Array<any>) {
    this._lastFlush = new Date().getTime();
    this._eventLoopStartTime = this._lastFlush;
    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
    );
    const result = moduleMethods[method].apply(moduleMethods, args);
    Systrace.endEvent();
    return result;
  }

而這一方法是在callFunctionReturnFlushedQueue()方法中調(diào)用的,它的代碼是

  callFunctionReturnFlushedQueue(module: string, method: string, args: Array<any>) {
    this.__guard(() => {
      this.__callFunction(module, method, args);
    });

    return this.flushedQueue();
  }

在調(diào)用 CallFunction 執(zhí)行 Js 后,會(huì)調(diào)用 flushedQueue 更新隊(duì)列。在__callFunction()方法中有這樣一段代碼

const result = moduleMethods[method].apply(moduleMethods, args);

真是通過apply的方法最終將調(diào)用傳遞到j(luò)s層的對(duì)應(yīng)組件的對(duì)應(yīng)方法上。這就是從<b>java層到j(luò)s層</b>的整個(gè)調(diào)用過程。

</br>
其中還有一個(gè)地方需要注意的是在<b>MessageQueue</b>對(duì)象中有一個(gè)屬性_callableModules數(shù)組,它就是用來存放哪些Javascript組件是可以被調(diào)用的,正常情況下_callableModules的數(shù)據(jù)和JavaScriptModules的數(shù)據(jù)是完全對(duì)應(yīng)的,也就是前文中從java層傳遞下來的JavaScriptModule信息JSON串所包含的信息。<b>MessageQueue</b>對(duì)象通過

const moduleMethods = this._getCallableModule(module);

來對(duì)照映射獲取這相應(yīng)模塊信息。
<h1>總結(jié)</h1>
前文描述了java->js調(diào)用各層的組成原理,和調(diào)用過程。在這里總結(jié)一下調(diào)用過程過程
<h5>java</h5>
1.<b>Catalystance</b>通過<b> JavaScriptModuleRegistry </b>調(diào)用對(duì)應(yīng)于js層組件的java層<b> JavaScriptModule </b>。
2.如果實(shí)例中緩存得有就返回相應(yīng)的JSModule,沒有就由<b> JavaScriptModuleInvocationHandler </b>通過動(dòng)態(tài)代理統(tǒng)一處理發(fā)向Javascript的所有調(diào)用。
3.<b>CatalystanceImpl</b> 進(jìn)一步將 調(diào)用拆分為moduleID,methodID 及 arguments 的形式轉(zhuǎn)交給 <b>ReactBridge</b> JNI 處理。

</br>
<h5>bridge</h5>
1.<b>OnLoad</b>接入<b>ReactBridge</b>傳入的調(diào)用,并通過<b> Bridge </b>,<b> JSExector</b>將傳入的的調(diào)用裝成Javascript執(zhí)行語句。
2.<b>JSCHelper</b>將調(diào)用交給<b>WebKit</b>內(nèi)核 通過evaluateScript 的方法將 moduleID,methodID 及 arguments 借助 JSC 傳遞給 Js 層。

</br>
<h5>js</h5>
1.調(diào)用通過<b> BatchedBridge </b>傳入<b> MessageQueue </b>它預(yù)先注冊(cè)了所有能夠接收通信請(qǐng)求的組件_callableModules ,同時(shí)也保存著來自Java層JavaScriptModule的兩張映射表。
2.<b> MessageQueue </b>接收到調(diào)用后,通過映射表確認(rèn)具體請(qǐng)求信息,然后通過apply方式調(diào)用js層相應(yīng)組件。

最后再回頭看各層的配圖,應(yīng)該對(duì)整個(gè)過程有更加清晰的理解了。這里只描述的java->js調(diào)用的過程,但RNA的通信機(jī)制是雙向的,js->java的調(diào)用及其各層的關(guān)鍵組成將在之后的學(xué)習(xí)探究中描述。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,697評(píng)論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,182評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,406評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,772評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,973評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評(píng)論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,644評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,953評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容