菜鸟学习React Native for Android 之通讯原理分析(JS调用Native)

前端之家收集整理的这篇文章主要介绍了菜鸟学习React Native for Android 之通讯原理分析(JS调用Native)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本文还是基于

React Native通讯原理

理解的一份个人笔记形式的博客

1.先上通讯总体框架图

2.再上Native调用JS的流程图

下面结合上面的图再结合ReactNative源码加以理解分析

思路分析:对于JS调用native来说,RN官方的思路是收集JAVA MODULE,在JS端生成一个JS对象,这个JS对象和这个JAVA模块类名相同或者相关,
这个JS对象再生成和native同名的function,例如这也 JSobj.methodname=function,于是在JS端就可以 调用这个JS的对象方法,而这个JS对象的每个方法
内部其实是负责把方法调用信息传递的messagequeue,然后调用JSCEExecutor注册在javascritCore的方法,传递给C++端,
C++端则根据JNI技术,结合反射可以调用到JAVA的方法,从而JavaModule中的方法得到执行。


那么我们结合源码看看JS是如何收集JAVA模型的呢,JAVA模型的根本来源还是在于Package包里面,例如MainReactPackage的getNativeModules,
在这里提供了
  1. @Override
  2. public List<ModuleSpec> getNativeModules(final ReactApplicationContext context) {
  3. return Arrays.asList(
  4. new ModuleSpec(AppStateModule.class,new Provider<NativeModule>() {
  5. @Override
  6. public NativeModule get() {
  7. return new AppStateModule(context);
  8. }
  9. }),new ModuleSpec(AsyncStorageModule.class,new Provider<NativeModule>() {
  10. @Override
  11. public NativeModule get() {
  12. return new AsyncStorageModule(context);
  13. }
  14. }),new ModuleSpec(CameraRollManager.class,new Provider<NativeModule>() {
  15. @Override
  16. public NativeModule get() {
  17. return new CameraRollManager(context);
  18. }
  19. }),new ModuleSpec(ClipboardModule.class,new Provider<NativeModule>() {
  20. @Override
  21. public NativeModule get() {
  22. return new ClipboardModule(context);
  23. }
  24. }),new ModuleSpec(DatePickerDialogModule.class,new Provider<NativeModule>() {
  25. @Override
  26. public NativeModule get() {
  27. return new DatePickerDialogModule(context);
  28. }
  29. }),new ModuleSpec(DialogModule.class,new Provider<NativeModule>() {
  30. @Override
  31. public NativeModule get() {
  32. return new DialogModule(context);
  33. }
  34. }),new ModuleSpec(FrescoModule.class,new Provider<NativeModule>() {
  35. @Override
  36. public NativeModule get() {
  37. return new FrescoModule(context);
  38. }
  39. }),new ModuleSpec(I18nManagerModule.class,new Provider<NativeModule>() {
  40. @Override
  41. public NativeModule get() {
  42. return new I18nManagerModule(context);
  43. }
  44. }),new ModuleSpec(ImageEditingManager.class,new Provider<NativeModule>() {
  45. @Override
  46. public NativeModule get() {
  47. return new ImageEditingManager(context);
  48. }
  49. }),new ModuleSpec(ImageLoaderModule.class,new Provider<NativeModule>() {
  50. @Override
  51. public NativeModule get() {
  52. return new ImageLoaderModule(context);
  53. }
  54. }),new ModuleSpec(ImageStoreManager.class,new Provider<NativeModule>() {
  55. @Override
  56. public NativeModule get() {
  57. return new ImageStoreManager(context);
  58. }
  59. }),new ModuleSpec(IntentModule.class,new Provider<NativeModule>() {
  60. @Override
  61. public NativeModule get() {
  62. return new IntentModule(context);
  63. }
  64. }),new ModuleSpec(LocationModule.class,new Provider<NativeModule>() {
  65. @Override
  66. public NativeModule get() {
  67. return new LocationModule(context);
  68. }
  69. }),new ModuleSpec(NativeAnimatedModule.class,new Provider<NativeModule>() {
  70. @Override
  71. public NativeModule get() {
  72. return new NativeAnimatedModule(context);
  73. }
  74. }),new ModuleSpec(NetworkingModule.class,new Provider<NativeModule>() {
  75. @Override
  76. public NativeModule get() {
  77. return new NetworkingModule(context);
  78. }
  79. }),new ModuleSpec(NetInfoModule.class,new Provider<NativeModule>() {
  80. @Override
  81. public NativeModule get() {
  82. return new NetInfoModule(context);
  83. }
  84. }),new ModuleSpec(PermissionsModule.class,new Provider<NativeModule>() {
  85. @Override
  86. public NativeModule get() {
  87. return new PermissionsModule(context);
  88. }
  89. }),new ModuleSpec(ShareModule.class,new Provider<NativeModule>() {
  90. @Override
  91. public NativeModule get() {
  92. return new ShareModule(context);
  93. }
  94. }),new ModuleSpec(StatusBarModule.class,new Provider<NativeModule>() {
  95. @Override
  96. public NativeModule get() {
  97. return new StatusBarModule(context);
  98. }
  99. }),new ModuleSpec(TimePickerDialogModule.class,new Provider<NativeModule>() {
  100. @Override
  101. public NativeModule get() {
  102. return new TimePickerDialogModule(context);
  103. }
  104. }),new ModuleSpec(ToastModule.class,new Provider<NativeModule>() {
  105. @Override
  106. public NativeModule get() {
  107. return new ToastModule(context);
  108. }
  109. }),new ModuleSpec(VibrationModule.class,new Provider<NativeModule>() {
  110. @Override
  111. public NativeModule get() {
  112. return new VibrationModule(context);
  113. }
  114. }),new ModuleSpec(WebSocketModule.class,new Provider<NativeModule>() {
  115. @Override
  116. public NativeModule get() {
  117. return new WebSocketModule(context);
  118. }
  119. }));
  120. }

和之前分享JAVA调用JS类似,也是在ReactInstanceManager的 processPackage方法里面把这些注册到CatalysInstance包含的NativeModuleRegistry里面

之前分析JS调用java的时候我们是从收集到的JS在JAVA原型产生动态代理供给使用,而限制我们要把这些收集的接口

RN在javaScriptCore设置了一个全局性的属性,__fbBatchedBridgeConfig,属性的值就是native module name列表。

在NativeModule.js中有这么一段是用来根据收集的JAVA模型信息产生JS funciion的
let NativeModules : {[moduleName: string]: Object} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else {
const bridgeConfig = global.__fbBatchedBridgeConfig;
invariant(bridgeConfig,'__fbBatchedBridgeConfig is not set,cannot invoke native modules');


(bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig,moduleID: number) => {
// Initially this config will only contain the module name when running in JSC. The actual
// configuration of the module will be lazily loaded.
const info = genModule(config,moduleID);
if (!info) {
return;
}

实际使用是这样的:
  1. class AwesomeProject extends Component {
  2. render() {
  3. return (
  4. <View style={styles.container}>
  5. <Text style={styles.welcome} onPress={onClick} >
  6. Welcome to React Native!
  7. </Text>
  8. <Text style={styles.instructions}>
  9. To get started,edit index.android.js
  10. </Double tap R on your keyboard to reload,{'\n'}
  11. Shake or press menu button for dev menu
  12. </TextInput />
  13. </View>
  14. );
  15. }
  16. }
  17.  
  18. function onClick(){
  19. var ToastAndroid = require('ToastAndroid')
  20. ToastAndroid.show('Click TextView...',ToastAndroid.SHORT);
  21. }

而show实际是是这样的


var RCTToastAndroid = require('NativeModules').ToastAndroid;
var ToastAndroid = {


// Toast duration constants
SHORT: RCTToastAndroid.SHORT,
LONG: RCTToastAndroid.LONG,


// Toast gravity constants
TOP: RCTToastAndroid.TOP,
BOTTOM: RCTToastAndroid.BOTTOM,
CENTER: RCTToastAndroid.CENTER,


show: function (
message: string,
duration: number
): void {
RCTToastAndroid.show(message,duration);
},


showWithGravity: function (
message: string,
duration: number,
gravity: number,
): void {
RCTToastAndroid.showWithGravity(message,duration,gravity);
},
};

调用的正是我们在nativeModules生成的对应JAVA接口的function,然后传回C++端,反射调用相应的JAVA实现

但是JS是如何拿到Native模块的详细信息的呢?
let NativeModules : {[moduleName: string]: Object} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else {
const bridgeConfig = global.__fbBatchedBridgeConfig;
invariant(bridgeConfig,cannot invoke native modules');


(bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig,moduleID: number) => {
// Initially this config will only contain the module name when running in JSC. The actual
// configuration of the module will be lazily loaded.
const info = genModule(config,moduleID);
if (!info) {
return;
}
remoteModuleConfig

if (info.module) {
NativeModules[info.name] = info.module;
}
// If there's no module config,define a lazy getter
else {
defineLazyObjectProperty(NativeModules,info.name,{
get: () => loadModule(info.name,moduleID)
});
}
});
}


module.exports = NativeModules;

bridgeConfig记录了原生模块的名字的集合,
第一句 const info = genModule(config,moduleID);这个是产生了一个info对象,有一个name属性,值就是模块名称
loadModule其实是调用了JSCExecutor在javascriptscore注册的一个函数,nativeRequireModuleConfig,
根据这个方法我们从ModuleRegistry中获取模块的详细信息

现在我的疑问是那个记录模块名称和记录记录模块集合的 ModuleRegistry是在哪里放入进去的呢?
其实这是在CatalysInstance实例时发生的,请看
  1. private CatalystInstanceImpl(
  2. ...
  3. mJavaRegistry.getModuleRegistryHolder(this));
  4. @H_502_946@mMainExecutorToken = getMainExecutorToken();
  5. }
我们调用了这个 getModuleRegistryHolder,那么我们看看getModuleRegistryHolder,
  1. /* package */ ModuleRegistryHolder getModuleRegistryHolder(
  2. CatalystInstanceImpl catalystInstanceImpl) {
  3. ...
  4. return new ModuleRegistryHolder(catalystInstanceImpl,javaModules,cxxModules);
  5. }
继续看看 ModuleRegistryHolder的构造函数
  1. public class ModuleRegistryHolder {
  2. private final HybridData mHybridData;
  3. private static native HybridData initHybrid(
  4. CatalystInstanceImpl catalystInstanceImpl,Collection<JavaModuleWrapper> javaModules,Collection<CxxModuleWrapper> cxxModules);
  5.  
  6. public ModuleRegistryHolder(CatalystInstanceImpl catalystInstanceImpl,Collection<CxxModuleWrapper> cxxModules) {
  7. mHybridData = initHybrid(catalystInstanceImpl,cxxModules);
  8. }
  9. }
  1. 看到了吧 我们调用JNI native initHybrid方法,把模块信息传递到C++了。

另外我们注意:
  1. 在JavaScriptCore中设置了全局属性__fbBatchedBridgeConfig,其值为Module Name列表
  2. 上面这句话是人家的原话,我们在JavaScirptCore中设置了个属性,我们其实是在JS中可以直接用的
关于Native接受JS调用
1. 主动调用指令会传递到MESSAGEQUEUE,然后准备调用JSC注册到ajvaScriptCore的方法,先判断上次调用的时候和本次调用的时间如果超过
5ms就调用,QUEUE传递过去后,产生JSON字符串,通过JstoNative调用到java
2. 被动,在某个适当的时候,具体啥时目前不清楚,会通过JSC调用到QUEUE在JAVAScriptCore注册的f方法,从而执行到JS里,然后把queue中剩余指令刷过去,
产生JSOn,通过JstoNative调用到java

猜你在找的React相关文章