React-Native之通信机制

近段时间来Android上最火的框架非react native莫属了,这里我不去评价这个框架的好坏,毕竟只有用过的人才会有深刻的体会。但是我个人有一个习惯,在使用一个开源库之前,一定要看过它的源码,不说百分百搞懂吧,至少得弄清楚它的工作原理,所以在使用RN之前我就看了看它的源码。不看不知道,一看吓一跳,它其中最核心的那一部分——java和js的通信写的确实是非常的精妙,把整个流程搞懂以后让我受益无穷。

这里插一句题外话,阿里的weex也马上就要开源了,我身边的小伙伴也有已经拿到源码投身于其中的,等到它开源的时候我也会去看看它的源码,了解下它和RN的区别到底是什么。

废话也不说了,让我们好好看看RN的通信机制吧。

前言

在看这篇文章之前,你得确保你有一定的RN开发基础,如果是零基础的同学,建议大家先去看看这个系列的文章,里面非常清楚的介绍了如何使用RN来进行开发。当然如果你不满足仅仅是[了解]的话,也可以去网上查阅关于RN的一些资料,其实里面一些东西是非常值得一看的,比如virtual
DOM,diff机制等等。

通信方式

我们所说的[通信],指的是RN中Java和js的通信,也就是js部分中的那些jsx代码是如何转化成一个java层真实的view和事件的,java层又是如何调用js来找出它所需要的那些view和事件的。

简单的说,RN的两端通信靠的是一张配置表,java端和js端持有同一张表,通信的时候就是靠这张表的各个条目的对应来进行的。

大致的就是和上面这张图一样,两端各持有一份相同的config,config中有一些已经注册的模块,两端的通信就是通过传输这样的“A”,“B”或者”C”来实现的。这个config对应到RN的代码是NativeModuleRegistry和JavaScriptModuleRegistry。如果大家想象不出来的话我可以给大家打个比喻,java端和js端的通信就好比一个中国人和一个美国人在对话,而这个config,也就是注册表就相当于两个翻译,有了翻译两个语言不通的人才能正常交流。那这两张表是如何生成的呢?还是让我们从代码中寻找答案吧。

首先我们知道在使用RN的时候,我们对应的activity要继承自ReactActivity并且重写一个叫做getPackages的方法。

1
2
3
4
5
6
@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage()
    );
}

这个MainReactPackage是RN帮我们生成好的,其中定义了一些基础的组件和事件,具体就不说了,大家可以自己去看一下源码。如果你想要自定义一些组件或者事件的话必须要自己去写一个package,至于怎么写大家看我前面提到的那一系列文章就知道了。而我们前面提到的那两个注册表——NativeModuleRegistry和JavaScriptModuleRegistry就是通过这样的package去生成的,具体方法我们看下去就知道了。

既然我们的activity继承自了ReactActivity,那我们就去看看ReactActivity里面做了什么。第一个要看的当然是onCreate函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
    // Get permission to show redbox in dev builds.
    if (!Settings.canDrawOverlays(this)) {
      Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
      startActivity(serviceIntent);
      FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
      Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
    }
  }

  mReactInstanceManager = createReactInstanceManager();
  ReactRootView mReactRootView = createRootView();
  mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
  setContentView(mReactRootView);
}

可以看到我们创建了一个ReactInstanceManager,看看是怎么创建的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected ReactInstanceManager createReactInstanceManager() {
  ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
      .setApplication(getApplication())
      .setJSMainModuleName(getJSMainModuleName())
      .setUseDeveloperSupport(getUseDeveloperSupport())
      .setInitialLifecycleState(mLifecycleState);

  for (ReactPackage reactPackage : getPackages()) {
    builder.addPackage(reactPackage);
  }

  String jsBundleFile = getJSBundleFile();

  if (jsBundleFile != null) {
    builder.setJSBundleFile(jsBundleFile);
  } else {
    builder.setBundleAssetName(getBundleAssetName());
  }

  return builder.build();
}

中间有一段这样的代码

1
2
3
for (ReactPackage reactPackage : getPackages()) {
  builder.addPackage(reactPackage);
}

通过builder模式把我们的package注入到了builder中并且最后调用build方法创建出一个ReactInstanceManagerImpl实例。

我们回过头来看onCreate函数,在这之后我们创建一个ReactRootView作为我们的根视图,并且调用它的startReactApplication函数,从函数名字就可以看出来,这个函数的作用非常重要,从这儿开始,算是启动了我们的RN程序。

在startReactApplication函数中我们调用了ReactInstanceManager的createReactContextInBackground()方法去构造属于RN程序的上下文。在这个方法中会去判断是否是开发模式,大家可以在自己的activity中重写getUseDeveloperSupport()去更改模式。模式不同的主要区别在于获取JSBundle的方式不同。如果你是开发模式的,就会从server端获取,如果不是,则是从文件中获取。这里我们只关注前者。最终会走到onJSBundleLoadedFromServer方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void onJSBundleLoadedFromServer() {
  recreateReactContextInBackground(
      new JSCJavaScriptExecutor.Factory(),
      JSBundleLoader.createCachedBundleFromNetworkLoader(
          mDevSupportManager.getSourceUrl(),
          mDevSupportManager.getDownloadedJSBundleFile()));
}

private void recreateReactContextInBackground(
      JavaScriptExecutor.Factory jsExecutorFactory,
      JSBundleLoader jsBundleLoader) {
      ..........

      mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
      mReactContextInitAsyncTask.execute(initParams);
  }

在该方法中我们调用JSBundleLoader的createCachedBundleFromNetworkLoader方法去创建了一个JSBundleLoader。它的主要作用是去加载JSBundle。大家可以去看看JSBundleLoader这个类,其中还有两种创建loader的方式,如果我们不是开发模式,调用的是createFileLoader,也就是说release的情况下我们需要用gradle生成了JSBundle之后将其放在assets目录上或者文件中。

下面让我们看看之后的recreateReactContextInBackground方法。

它会调用了一个叫做mReactContextInitAsyncTask的AsyncTask去执行异步任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
  Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
  try {
    JavaScriptExecutor jsExecutor =
        params[0].getJsExecutorFactory().create(
          mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap());
    return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
  } catch (Exception e) {
    // Pass exception to onPostExecute() so it can be handled on the main thread
    return Result.of(e);
  }
}

我们可以看到它的doInBackground方法调用了createReactContext()方法去创建上下文。

1
2
3
4
5
6
7
8
9
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
private ReactApplicationContext createReactContext(
    JavaScriptExecutor jsExecutor,
    JSBundleLoader jsBundleLoader) {
  FLog.i(ReactConstants.TAG, "Creating react context.");
  ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
  mSourceUrl = jsBundleLoader.getSourceUrl();
  NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
  JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

  ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
  if (mUseDeveloperSupport) {
    reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
  }

  ReactMarker.logMarker(PROCESS_PACKAGES_START);
  Systrace.beginSection(
      Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
      "createAndProcessCoreModulesPackage");
  try {
    CoreModulesPackage coreModulesPackage =
        new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
    processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
  } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
  }

  // TODO(6818138): Solve use-case of native/js modules overriding
  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);
    }
  }
  ReactMarker.logMarker(PROCESS_PACKAGES_END);

  ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
  Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
  NativeModuleRegistry nativeModuleRegistry;
  try {
     nativeModuleRegistry = nativeRegistryBuilder.build();
  } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
  }

  ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_START);
  Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
  JavaScriptModulesConfig javaScriptModulesConfig;
  try {
    javaScriptModulesConfig = jsModulesBuilder.build();
  } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_END);
  }

  NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
      ? mNativeModuleCallExceptionHandler
      : mDevSupportManager;
  CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
      .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
      .setJSExecutor(jsExecutor)
      .setRegistry(nativeModuleRegistry)
      .setJSModulesConfig(javaScriptModulesConfig)
      .setJSBundleLoader(jsBundleLoader)
      .setNativeModuleCallExceptionHandler(exceptionHandler);

  ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
  // CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
  Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
  CatalystInstance catalystInstance;
  try {
    catalystInstance = catalystInstanceBuilder.build();
  } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
  }

  if (mBridgeIdleDebugListener != null) {
    catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
  }

  reactContext.initializeWithInstance(catalystInstance);

  ReactMarker.logMarker(RUN_JS_BUNDLE_START);
  Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
  try {
    catalystInstance.runJSBundle();
  } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(RUN_JS_BUNDLE_END);
  }

  return reactContext;
}

这个方法的代码就比较多了,但是我们现在只看我们所关注的。大家应该还记得我们的关注点吧?[两个注册表NativeModuleRegistry和JavaScriptModuleRegistry是如何生成的]。这里给出了答案。

1
2
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

首先创建出两个builder。

1
2
3
4
5
6
7
try {
  CoreModulesPackage coreModulesPackage =
      new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
  processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
  Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}

然后会去new一个CoreModulesPackage并且使用了processPackage方法去处理它,这个CoreModulesPackage里面定义了RN框架核心的一些Java和JS的module。

下面让我们看看processPackage方法。

1
2
3
4
5
6
7
8
9
10
11
12
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);
  }
}

很简单,拿到具体的native和JS的module把它们添加到对应的builder中。

再处理完了CoreModulesPackage之后,程序又会去处理我们在activity中注入的那些package。

1
2
3
4
5
6
7
8
9
10
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);
  }
}

接下去就是生成注册表了。

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
   nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
   Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
   ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}

try {
   javaScriptModulesConfig = jsModulesBuilder.build();
} finally {
   Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
   ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_END);
}

至此,我们就把所有的packages,包括RN核心的CoreModulesPackage和我们activity自己注入的package里面的各个modules全部写到了对应Registry的builder中。

现在这两份注册表是存在于java端的,那要怎么传输到JS端呢?我们继续看下去。

再创建完了对应的注册表之后,ReactInstanceManagerImpl会通过builder模式去创建一个CatalystInstance的实例CatalystInstanceImpl。在CatalystInstanceImpl的构造函数中会去创建一个ReactBridge。并且会调用initializeBridge(jsExecutor, jsModulesConfig)这个方法。在这个方法中会去通过ReactBridge向C层传递一些数据,其中有这么一段:

1
2
3
bridge.setGlobalVariable(
    "__fbBatchedBridgeConfig",
    buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));

调用了bridge的setGlobalVariable方法,这是一个native的方法。

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

这个方法的作用就是先把JavaRegistry格式化成json,并且调用C层的代码传输到js端。由于本人C层学艺不精,能看懂个大概,怕会有细节讲错,就不带大家进行分析了。

总结

到这里,真相就水落石出了,我们来总结一下吧。

(1) 在程序启动的时候,也就是ReactActivity的onCreate函数中,我们会去创建一个ReactInstanceManagerImpl对象

(2) 通过ReactRootView的startReactApplication方法开启整个RN世界的大门

(3) 在这个方法中,我们会通过一个AsyncTask去创建ReactContext

(4) 在创建ReactContext过程中,我们把我们自己注入(MainReactPackage)的和系统生成(CoreModulesPackage)的package通过processPackage方法将其中的各个modules注入到了对应的Registry中

(5) 最后通过CatalystInstanceImpl中的ReactBridge将java的注册表通过jni传输到了JS层。

这样,js层就获取到了java层的所有接口和方法,相当于一个美国人身边有了以为中文翻译。而js层的注册表本来就是由java层生成的,所以就相当于一个中国人身边有了一个英文翻译,从此他们就可以愉快的交流了。

涉及到的重要的类:

ReactInstanceManagerImpl,ReactContext,CatalystInstanceImpl,ReactBridge。

Java->js

前面我们讲了两端通信的方式和注册表是如何从Java端发送到js端的,下面让我们讲讲这样的准备工作完成以后,java是如何调用js的方法的。

首先,经过上面的学习,其实这个问题已经有了一个初步的答案了,因为[将JavaRegistry从java端传输到js端]这个过程就是一个java向js通信的过程,具体的过程在上一节的总结中已经说了,让我们回想一下,之前所有的事情都是在ReactInstanceManagerImpl中ReactContextInitAsyncTask的doInBackground方法中完成的,那这个方法完成之后又做了什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onPostExecute(Result<ReactApplicationContext> result) {
  try {
    setupReactContext(result.get());
  } catch (Exception e) {
    mDevSupportManager.handleException(e);
  } finally {
    mReactContextInitAsyncTask = null;
  }

  // Handle enqueued request to re-initialize react context.
  if (mPendingReactContextInitParams != null) {
    recreateReactContextInBackground(
        mPendingReactContextInitParams.getJsExecutorFactory(),
        mPendingReactContextInitParams.getJsBundleLoader());
    mPendingReactContextInitParams = null;
  }
}

可以看到,在onPostExecute方法中调用了setupReactContext方法,在这个方法中会去调用attachMeasuredRootViewToInstance方法。

1
2
3
4
5
6
7
8
private void attachMeasuredRootViewToInstance(
    ReactRootView rootView,
    CatalystInstance catalystInstance) {
  .......
  .......

  catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}

这个方法的最后用了我们的CatalystInstanceImpl的getJSModule方法,它会去调用JavaScriptModuleRegistry的getJSModule方法,获取对应的JavaScriptModule,也就是从注册表中获取对应的模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public synchronized  <T extends JavaScriptModule> T getJavaScriptModule(ExecutorToken executorToken, Class<T> moduleInterface) {
  HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> instancesForContext =
      mModuleInstances.get(executorToken);
  if (instancesForContext == null) {
    instancesForContext = new HashMap<>();
    mModuleInstances.put(executorToken, instancesForContext);
  }

  JavaScriptModule module = instancesForContext.get(moduleInterface);
  if (module != null) {
    return (T) module;
  }

  JavaScriptModuleRegistration registration =
      Assertions.assertNotNull(
          mModuleRegistrations.get(moduleInterface),
          "JS module " + moduleInterface.getSimpleName() + " hasn‘t been registered!");
  JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
      moduleInterface.getClassLoader(),
      new Class[]{moduleInterface},
      new JavaScriptModuleInvocationHandler(executorToken, mCatalystInstance, registration));
  instancesForContext.put(moduleInterface, interfaceProxy);
  return (T) interfaceProxy;
}

这个方法就比较神奇了,首先去缓存中找,如果找到就返回,没找到就去创建,怎么创建的呢,用的是动态代理!

1
2
3
4
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
    moduleInterface.getClassLoader(),
    new Class[]{moduleInterface},
    new JavaScriptModuleInvocationHandler(executorToken, mCatalystInstance, registration));

这里大家必须要对动态代理有所了解,可以自己去找相关的知识。让我们看看JavaScriptModuleInvocationHandler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  ExecutorToken executorToken = mExecutorToken.get();
  if (executorToken == null) {
    FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
    return null;
  }
  String tracingName = mModuleRegistration.getTracingName(method);
  mCatalystInstance.callFunction(
    executorToken,
      mModuleRegistration.getModuleId(),
      mModuleRegistration.getMethodId(method),
      Arguments.fromJavaArgs(args),
      tracingName);
  return null;
}

我们看它最核心的invoke方法。里面获取了调用方法的moduleId,methodId和参数,然后调用了CatalystInstanceImpl的callFunction去执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void callFunction(
    ExecutorToken executorToken,
    int moduleId,
    int methodId,
    NativeArray arguments,
    String tracingName) {
  synchronized (mJavaToJSCallsTeardownLock) {
    if (mDestroyed) {
      FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
      return;
    }

    incrementPendingJSCalls();

    Assertions.assertNotNull(mBridge).callFunction(executorToken, moduleId, methodId, arguments, tracingName);
  }
}

直接调用了ReactBridge的同名函数。

1
public native void callFunction(ExecutorToken executorToken, int moduleId, int methodId, NativeArray arguments, String tracingName);

可以看到又是一个native函数,具体作用就是将想用调用的方法对应的moduleId,methodId和arguments通过jni传递到js端进行调用。而我们这边调用的是AppRegistry的runApplication方法,这个方法在js端的作用就是开始运行整个js程序,从而将我们的RN程序真正的跑起来。

总结

首先,对于我们java端要调用的js端的类和方法,我们都必须要注册到js的注册表中,这个过程在上一部分的分析中已经带大家走过了。当真正要调用的时候,步骤是这样的:

(1) 调用CatalystInstanceImpl这个类的getJSModule方法去得到对应的JSModule

(2) CatalysInstanceImpl实际上是调用JavaScriptModuleRegistry的getJSModule方法去获取注册在其中的module

(3) 然后通过动态代理拿到方法的各种参数,包括moduleId,methodId和params

(4) 通过ReactBridge调用jni传递到C层

(5) 通过C层再传递到js层。

涉及到的重要的类:

CatalystInstanceImpl,JavaScriptModuleRegistry,JavaScriptModuleInvocationHandler,ReactBridge。

通过这个图配合上代码应该能比较好的理解了。

js->Java

RN的js调java的流程可以说是让我觉得最新颖的地方,具体就是js不是直接通过注册接口去调用java方法的,而是将对应的的参数(moduleId和methodId)push到一个messageQueue中,等待java层的事件来驱动它,当java层的事件传递过来以后,js层把messageQueue中所有的数据返回给java层,再通过注册表JavaRegistry去调用方法。

首先,我们说了js层是把对应的参数push到messageQueue中,具体的方法是MessageQueue.js的__nativeCall方法。

1
2
3
4
5
6
7
8
9
10
__nativeCall(module, method, params, onFail, onSucc) {

.........

this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(params);

...........
}

可以看到它把对应的module,method和params push到了队列里面,然后就是等待java层的事件驱动。

java层的事件驱动其实也可以看成java层向js层的通信,最终会走到MessageQueue.js的callFunctionReturnFlushedQueue方法和invokeCallbackAndReturnFlushedQueue方法。

1
2
3
4
5
6
7
8
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
  this.__callFunction(module, method, args);
  this.__callImmediates();
});

return this.flushedQueue();
}

调用了flushedQueue将MessageQueue中的所有数据通过C层发往java层。

到了java层以后,会回调到NativeModulesReactCallback类执行call方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private class NativeModulesReactCallback implements ReactCallback {

  @Override
  public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
    mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();

    synchronized (mJSToJavaCallsTeardownLock) {
      // Suppress any callbacks if destroyed - will only lead to sadness.
      if (mDestroyed) {
        return;
      }
      mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
    }
  }
  .....
}

可以看到里面通过JavaRegistry调用了它的call方法。

1
2
3
4
5
6
7
8
9
10
11
12
/* package */ void call(
    CatalystInstance catalystInstance,
    ExecutorToken executorToken,
    int moduleId,
    int methodId,
    ReadableNativeArray parameters) {
  ModuleDefinition definition = mModuleTable.get(moduleId);
  if (definition == null) {
    throw new RuntimeException("Call to unknown module: " + moduleId);
  }
  definition.call(catalystInstance, executorToken, methodId, parameters);
}

拿到对应的module,调用call方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void call(
      CatalystInstance catalystInstance,
      ExecutorToken executorToken,
      int methodId,
      ReadableNativeArray parameters) {
    MethodRegistration method = this.methods.get(methodId);
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
    try {
      this.methods.get(methodId).method.invoke(catalystInstance, executorToken, parameters);
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }
}

其中根据methodId拿到对应module的方法,执行invoke。

最终执行的是BaseJavaModule的invoke方法。

1
2
3
4
5
6
7
@Override
public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {

	.........
	mMethod.invoke(BaseJavaModule.this, mArguments);
	.........
}

通过反射调用最终的方法。

总结

这里我们重点要了解的就是js向java的通信靠的是事件驱动模式。

(1) JS将方法的对应参数push到MessageQueue中,等java端事件传递

(2) Java端事件触发以后,JS层将MessageQueue中的数据通过C层传递给java层

(3) C层调用一开始注册在其中的NativeModulesReactCallback

(4 然后通过JavaRegistry这个Java注册表拿到对应的module和method

(5) 通过反射执行方法。

涉及到的重要的类:

MessageQueue(JS层),NativeModulesReactCallback,JavaRegistry。

老规矩,通过图配合代码来理解流程。

重要类的作用

最后我们通过总结一下前面提到的几个重要的类的作用来加深印象。

ReactInstanceManager:它的作用是创建出ReactContext,CatalystInstance等类,解析package生成注册表,并且配合ReactRootView管理View的创建,生命周期等功能。

ReactContext:继承自ContextWrapper,是RN程序自己的上下文,我们可以通过getContext()去获得,里面有CatalystInstance实例,可以去获得Java和JS的module。

ReactRootView:RN程序的根视图,startReactApplication方法开启RN世界的大门。

CatalystInstance:Java端通信的管理类,提供通信的环境,方法和回调方法,内部通过ReactBridge进行通信。

ReactBridge:通信的核心类,通过jni的方式进行通信。

NativeModuleRegistry:Java接口的注册表。

JavascriptModuleRegistry:JS接口的注册表。

CoreModulePackage:RN核心框架的package,包括Java接口和js接口,前文提到的AppResgitry就是在这里面注册的。

MainReactPackage:RN帮我们封装的一些通用的Java组件和事件。

JsBundleLoader:用于加载JSBundle的类,其中会根据应用的情况创建不同的loader。

JSBundle:存有js核心逻辑,在release环境下要通过gradle任务去创建并且放在对应的目录下。

参考文章

深入源码探索
ReactNative 通信机制

时间: 2024-08-08 15:14:09

React-Native之通信机制的相关文章

react native组件通信

在日常开发过程中,组件之间的通信我们应该经常用到,也是我们开发过程中不可或缺的一部分.组件可以分为父子组件以及兄弟组件,下面就不同组件之间的通信进行说明. 父组件通过props与子组件通信 定义父组件 import React, {Component} from 'react'; import { Text, StyleSheet, View, TouchableOpacity } from 'react-native'; import Child from "./Child"; ex

【React Native】在原生和React Native间通信

一.从React Native中调用原生方法(原生模块) 原生模块是JS中也可以使用的Objective-C类.一般来说这样的每一个模块的实例都是在每一次通过JS bridge通信时创建的.他们可以导出任意的函数和常量给React Native.相关细节可以参阅这篇文章. 在React Native中,一个“原生模块”就是一个实现了“RCTBridgeModule”协议的Objective-C类,其中RCT是ReaCT的缩写. // CalendarManager.h #import <Reac

React于React native的渲染机制

面向virtual DOM编程 vs 面向native componet编程: 状态编程引起的UI变化会全部提交到native compnent然后走平台原来的渲染流程. The DOM is just one of the rendering environments React can render to, the other major targets being native iOS and Android views via React Native. (This is why "vi

React Native 调研报告

Facebook三月份开源了React Native iOS平台的框架,让移动开发人员和web开发者都各自兴奋了一把:native的移动开发者想的比较多的估计是Facebook的那句:"learn once, write everywhere",而web开发者兴奋的估计是,不需要学习iOS那陌生的OC或者swift语言,用自己熟悉的javascript语言就可以开发原生的移动APP了.那么新推出的react native 能否承载的了两大阵营的开发者的期待了.本人及同事对react n

【腾讯Bugly干货分享】React Native项目实战总结

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 "8小时内拼工作,8小时外拼成长"这是大家共同的理想.除了每天忙于工作外,我们都希望能更多地区吸收领域内的新知识与新技能,从而走向人生巅峰. Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是经过审核的移动开发工程师.每周都会举行嘉宾分享,话题讨论等活动. 上一期我们邀请了腾讯SNG工程师&qu

React Native 从入门到原理一

React Native 从入门到原理一 React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几. 本文分为两个部分:上半部分用通俗的语言解释了相关的名词,重点介绍 React Native 出现的背景和试图解决的问题.适合新手对 React Native 形成初步了解. 下半部分则通过源码(0.27 版本)分析 React Native 的工作原理,适合深入学习理解 React Na

React Native通信机制详解

本文转载至 http://blog.cnbang.net/tech/2698/?from=groupmessage&isappinstalled=1 React Native是facebook刚开源的框架,可以用javascript直接开发原生APP,先不说这个框架后续是否能得到大众认可,单从源码来说,这个框架源码里有非常多的设计思想和实现方式值得学习,本篇先来看看它最基础的JavaScript-ObjectC通信机制(以下简称JS/OC). 概览 React Native用iOS自带的Java

react native js 与 native 的通信与交互方式

JS 的启动过程 React Native 的 iOS 端代码是直接从 Xcode IDE 里启动的.在启动时,首先要对代码进行编译,不出意外,在编译后会弹出一个命令行窗口,这个窗口就是通过 Node.js 启动的 development server . 问题是这个命令行是怎么启动起来的呢?实际上,Xcode 在 Build Phase 的最后一个阶段对此做了配置,其实就是增加了一个 sh 脚本,让小葱的在编译会自动去执行这个脚本,打开 npm,相当于你直接手动命令行切到 react-nati

React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)

[工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 有了前面<React Native Android 从学车到补胎和成功发车经历>和<React Native Android Gradle 编译流程浅析>两篇文章的学习我们 React Native 已经能够基本接入处理一些事情了,那接下来的事情就是渐渐理解 RN 框架的一些东西,以便裁剪和对 RN 有个更深入的认识,所以本篇总结了我这段时间阅读源码

[技术博客]react native事件监听、与原生通信——实现对通知消息的响应

在react native中会涉及到很多页面之间的参数传递问题.静态的参数传递通常利用组件的Props属性,在初始化组件时即可从父组件中将参数传递到子组件中.对于非父子关系的组件来说,无法直接传递参数,此时可能会用到react-navigation来传递:此外,若要将异步函数.不可预料的事件执行等得到的参数用于页面刷新时,前述的方法都不太奏效. DeviceEventEmitter react-native中采用了DeviceEventEmitter来实现对事件的监听,实现非父子关系的页面之间的