React-Native最核心的是Native与Javascript之间的通信,而且是双向通信,Native层到Javascript层,Javascript层到Native层,虽说是两个方向,但实现上大同小异,我们先从Native层入手,研究一下Native调用Javascript的过程。
1、通信模型
Android应用层的程序语言是Java,React-Native在Native端的框架实现用的也是Java语言,所以实质上是Java与Javascript两种程序语言的调用。
其实这个过程,在Android系统上已经有了实现,就是WebView,熟悉WebView的都知道底层实现是WebKit,尽管在Android 4.4系统上切换成了Chromium,但归根结底还是WebKit的变种,只是加了谷歌自己的一些东西。然而React-Native与WebView并没有一点关系,而且后者的WebKit内核也不支持ES6特性(React语法大多基于ES6),那怎么办?只能自己弄一套最新的WebKit作为React-Native的解释器了,这个从安卓工程lib目录下面的libjsc.so动态链接库文件可以印证,这样做还有两个重要好处就是兼容绝大多少设备版本和方便添加自定义功能。
所以由此,我们大概可以猜到React-Native的通信原理,画一张图来简单地描述一下:
2、Java层实现
之前说过,React-Native的重要设计思想是组件化,为了便于维护扩展和降低耦合,React-Native并没有为了实现某一具体的通信编写代码(比如上篇博文所讲的触摸事件传递),而是设计了一套标准用于组件化。这套标准是向开发者开放的,开发者可以自行编写需要的组件用来在Native与Javascript之间通信,虽然这并不是推荐的选择。
2.1 JavaScriptModule组件
React-Native官方实现了一定数量的组件,比如触摸事件组件,按键组件等,这些组件都位于CoreModulesPackage中,属于默认加载的。所有的组件都必须继承JavaScriptModule接口标准。JavaScriptModule位于com.facebook.react.bridge包下面:
/**
* 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.
*
* NB: JavaScriptModule does not allow method name overloading because JS does not allow method name
* overloading.
*/
@DoNotStrip
public interface JavaScriptModule {
}
阅读一下注释,主要有三点信息:
1、所有组件必须继承JavaScriptModule,并注册在CatalystInstance中。
2、所有public方法与Javascript层保持同名并由后者具体实现。
3、由于Javascript不支持重载,所以Java中也不能有重载。
仔细的读者会发现,注释里有两个单词非常关键。extending和implemented 。Java层extend,Javascript层implement,也就是说Java层只做接口定义,而实现由Javascript完成。所以,搜索一下JavaScriptModule的子类会发现它们都是接口,没有具体实现类。
有点晦涩但其实很好理解,举个简单的例子。去餐馆吃饭,顾客(Java)只要定义(interface)好想吃什么菜,然后和餐馆(Bridge)说,餐馆会通知自己的厨师(Javascript)把具体的菜做好。这就是一个简单的通信过程Java->Bridge->Javascript。
2.2 JavaScriptModule组件的注册
上一篇文章讲的触摸事件的处理,里面提到一个RCTEventEmitter的类,用来将每个触摸事件都传递给Javascript层,这个组件就是继承于JavaScriptModule。
public interface RCTEventEmitter extends JavaScriptModule {
public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
public void receiveTouches(
String eventName,
WritableArray touches,
WritableArray changedIndices);
}
先前注释中第1点,所有JavaScriptModule组件都必须在CatalystInstance中注册,那我们来看一下注册的过程。
RCTEventEmitter是facebook官方定义的,组装在CoreModulesPackage中,而所有的package都是在com.facebook.react.ReactInstanceManagerImpl中处理的,看一下代码:
class ReactInstanceManagerImpl extends ReactInstanceManager {
...
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
...
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
...
}
for (ReactPackage reactPackage : mPackages) {
...
try {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
...
}
}
}
private void processPackage(ReactPackage reactPackage, ReactApplicationContext reactContext, NativeModuleRegistry.Builder nativeRegistryBuilder, JavaScriptModulesConfig.Builder jsModulesBuilder) {
...
for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()){
jsModulesBuilder.add(jsModuleClass);
}
}
...
}
可以看到CoreModulesPackage和开发者扩展自定义的mPackages都是通过processPackage方法里添加到JavaScriptModulesConfig里注册的。
简单的建造者模式,我们直接看一下JavaScriptModulesConfig类,位于包com.facebook.react.bridge下。
public class JavaScriptModulesConfig {
private final List<JavaScriptModuleRegistration> mModules;
private JavaScriptModulesConfig(List<JavaScriptModuleRegistration> modules) {
mModules = modules;
}
/*package*/ List<JavaScriptModuleRegistration> getModuleDefinitions() {
return mModules;
}
...
}
JavaScriptModule明显是通过构造函数传入,然后又通过一个getter方法提供出去了,看样子JavaScriptModulesConfig只起到了一个中间者的作用,并不是真正的注册类。
回看一下之前的ReactInstanceManagerImpl类代码,createReactContext中还有一段,如下:
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader)
...
JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();
JavaScriptModulesConfig javaScriptModulesConfig;
try {
javaScriptModulesConfig = jsModulesBuilder.build();
} finally {
...
}
...
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
...
CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
...
}
...
}
看来最终javaScriptModulesConfig是用来构建CatalystInstance的,正如注释所讲,果然没有骗我。
CatalystInstance只是一个接口,实现类是CatalystInstanceImpl,同样位于包com.facebook.react.bridge下。Catalyst单词的中文意思是催化剂,化学中是用来促进化学物之间的反应,难道说CatalystInstance是用来催化Native和Javascript之间的反应?让我们来瞧一瞧真面目吧。
public class CatalystInstanceImpl implements CatalystInstance {
...
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModulesConfig jsModulesConfig,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
...
mJSModuleRegistry = new JavaScriptModuleRegistry(Catalyst