React Native与原生模块、组件之间的关系浅析(二)

那么书接上回,今天主要是继续探究React Native与原生模块的架构方式。

原生模块

原生模块可以访问activity、运行环境、GPS、存储空间等。原生模块就是能够在JavaScript层调用的API。

因为对原生模块的全部请求都要异步执行。如果原生方法需要为JavaScript层的调用返回数据,该操作将通过promise或者回调函数来完成。React Native为这两种方式都提供了接口。

剖析原生模块

IOSAndroid对原生模块的实现思路是这样的:

  1. 每个模块继承自原生模块父类。
  2. 每个模块都定义了名称,以便JavaScript层访问。
  3. 每个模块都导出可调用的方法,并包含Java的注释或Objective-C的宏。

01. Android

Android平台的原生模块继承ReactContextBaseJavaModule ,并且必须实现getName 方法,它的返回值作为JavaScript模块的名称。

任何允许JavaScript层调用的方法,都带有@ReactMethod 注释。React的内部机制会把JavaScript层的请求映射到这些方法上。每个方法的身份由它的签名、名称以及参数进行鉴别。方法所需的任何参数都会被转换成对应的属性类型。

简单的示例在上一篇已经提及,请参阅React Native与原生模块、组件之间的关系浅析

02. JavaScript

JavaScript层把原生模块作为NativeModules 对象的一个属性,并且任何带有@ReactMethod 注释或者属于RCT_EXPORT_METHOD宏的方法都能够被调用。

参数

原生模块的方法和普通方法一样可以接受参数。

针对于Android,带有@ReactMethod注释的方法支持以下参数类型。

  • Boolean -> Bool
  • Integar -> Number
  • Double -> Number
  • Float -> Number
  • String -> String
  • Callback -> function
  • ReadableMap -> Object
  • ReadableArray -> Array

回调函数和promise

由于所有的通信过程都是异步的,原生模块不能有返回值。React Native使用回调函数和promise类作为解决方案。使用方式与在JavaScript代码中执行回调函数以及promiseresolve/reject 操作完全一样。

01 回调函数

原生回调函数接口遵循两个参数的约定:
- 第一参数表示错误对象(没有错误的情况则为null)。
- 第二参数用来提供要响应的数据。

示例:

@ReactMethod
public void add (int a, int b, Callback callback) {
  int sum = a + b;
  callback.invoke(null, sum);
}

02 promise

promise可以在操作成功后把值返回给JavaScript层。响应要么成功要么失败。JavaScript层的接口保留了我们所熟悉的.then.catch方法。

示例:

@ReactMethod
public void failIfFalse (boolean value, Promise promise) {
  if (value) {
    promise.resolve("Your value was true, so it resolved.");
  } else {
    promise.reject(new Error("Your value was false, so it rejected"));
  }
}

03 返回请求成功的对象

我们已经了解到,可以通过回调函数和promise在请求成功后把标量值返回给JavaScript层。大多数情况下响应内容应该是JSON对象的形式。

Android提供了WritableMap 来返回JSON响应。该接口和包含特定类型put方法的Map很相似。

  • pushNull
  • putBoolean
  • putDouble
  • putInt
  • putString
  • putArray
  • putMap

String类型的键名作为每个方法的第一参数。
第二参数是方法名相关类型的值。

示例:

@ReactMethod
public void jsonResponse(Promise promise) {
  WritableMap jsonMap = new WritableNativeMap();
  jsonMap.putString("stringValue", "A string");
  jsonMap.putBoolean("booleanValue", true);
  jsonMap.putInt("intValue", 123);
  promise.resolve(jsonMap);
}

常量

原生模块也可以导出常量,供JavaScript访问。

要使原生模块的常量可用,需要实现父类的getConstants (Android)或constantsToExport (iOS)方法,这两个方法将返回键值对的映射,键名会作为JavaScript模块的属性而存在。

示例:

@Override
public Map<String, Object> getConstants() {
  final Map<String, Object> constants = new HashMap<>();
  constants.put("HELLO", "world");
  constants.put("FOO", "bar");
}

事件

DeviceEventEmitter模块用于把事件从原生层传到JavaScript层。

针对Android而言,ReactContext暴露出getJSModule 方法用于获取其他模块,为它传入类参数后返回相应的实例。它将用来获取RCTDeviceEventEmitter模块并调用emit 方法,接着事件就会通过桥接层发送到JavaScript层,DeviceEventEmitter模块的监听器就会捕获这个事件,并通知其他任意的事件监听器。

示例:

@ReactMethod
public void sendTestEvent() {
  String eventName = "customEvent";
  WritableMap params = new WritableNativeMap();
  params.putString("param", "Hello world");
 
  getReactApplicationContext()
    .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
    .emit(eventName, params);
}

JavaScript上面的调用方式如下:

import {DeviceEventEmitter, NativeModules} from 'react-native';
const {ExampleModule} = NativeModules;
DeviceEventEmitter.addListener('customEvent', e => {
  console.log('received event', e.nativeEvent.param); // 2
});
 
ExampleModules.sendTestEvent(); // 1

总结

通过本篇的学习,了解了原生模块和组件的工作原理。知道了react native与原生层的调用关系。原生模块就是JavaScript层可以调用的一些方法的集合。

猜你喜欢

转载自blog.csdn.net/r122555/article/details/80972607