-2,前言
最近我们公司突然开始涉足react-native开发,老实说,我内心是拒绝的。其一,是因为目前我对于原生开发还不够精通,不想突然转向,其二是因为react-native目前还没有1.0的正式版本,仍然处于探索期不稳定。其三,我特么开发react-native用的是windows啊,人家facebook的工程师开发这玩儿用的都是mac。看在工资的份儿上,我开始探索react-native。
-1,解释说明
- 说实话,一开始就上这么难的话题,我也是没办法。关于环境集成,跑demo等等遇到的这些坑,虽然积攒了一点经验,但是很多时候,我是懵逼的,所以总结这些经验而不说出问题的根源,不去探究为什么,这不是我的风格。而通信机制则是一整套严肃的设计,知根知底,而且随着对于原理的深究,以后对于问题的根源就会更加明了(不过windows环境下还是不要立这种flag比较好)。
- 其次,作为react-native的菜鸟,我驾驭这个问题是有一些难度的,但是,这也正好是另一种优势,那就是,我能以一个react-native的菜鸟的角度,去解释Java与js的通信机制,应该是对于刚接触react-native的原生开发的工程师更加友好。
- 最后,我在探究这个问题的过程中,找到了很多很棒的博文,比如其实没那么复杂!探究react-native通信机制等等,对于这些前辈,我只有献上自己的膝盖。 我目前用的RN的最新版本,而他们的版本应该是有点老,因此代码其实跟上面的作者的代码有些许出入。
0,铺垫
首先我们要明白以下几点:
- 在react-native中的通信,主要是Java与JavaScript之间的通信,而实际上,Java与Js之间是根本没办法直接对话的,别看他们看起来好像是亲戚,实际上他们的关系就相当于雷锋和雷峰塔的关系
- 那么Java和Js之间想要能听懂对方的话,有两个必备条件:
- 双方的信息要能够传达到对方那里去,就是,先不管听不听的懂 ,你首先要把话传过去
- 信息传达前需要经过翻译,才能被接受方正确理解。
- 第一个条件的解决方案是通过C++来做这个传话筒,Java通过JNI来call到c++层,然后c++层再把信息传到js,反之亦然;第二个条件的解决方案就是通过在初始化的时候构造两本“词典”,约定好以后说话只说对方写好的“词典”上的单词,保证对方能听懂。
所以我们的问题其实只有两点:那就是集中精力观察“词典”是怎么传递到双方手里的,以及两方是怎么传递数据的。
1,开篇
1.1,Java传递“词典”
首先,对于词典还是正确解释一下,它是某种config,某种配置文件,每次Java层收到js层传来的的信息,都会读取这个文件,然后才能理解Java层的意思。Java层也是一样。他们对应RN的代码的类分别是:NativeModuleRegistry和JavaScriptModuleRegistry
初始化的开端源自ReactActivity,这是react-native中的类,它的onCreate()方法中是这么做的:
@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();
}
}
mReactRootView = createRootView();
//这是最重要的一步
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
getMainComponentName(),
getLaunchOptions());
setContentView(mReactRootView);
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}复制代码
mRootView是一个layout,继承自FrameLayout,一切的js渲染从这个Layout上开始,它的startReactApplication()方法如下:
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
UiThreadUtil.assertOnUiThread();
// TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap
// here as it may be deallocated in native after passing via JNI bridge, but we want to reuse
// it in the case of re-creating the catalyst instance
Assertions.assertCondition(
mReactInstanceManager == null,
"This root view has already been attached to a catalyst instance manager");
mReactInstanceManager = reactInstanceManager;
mJSModuleName = moduleName;
mLaunchOptions = launchOptions;
//这是关键
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
// We need to wait for the initial onMeasure, if this view has not yet been measured, we set which
// will make this view startReactApplication itself to instance manager once onMeasure is called.
if (mWasMeasure