安卓系统为我们提供了webview来加载网页,为了让webview加载的网页可以与App交互,系统提供了一套机制帮助我们更方便的实现通信。同样为了实现React Native与原生App之间的通信,Facebook也实现了自己的一套交互机制.
通过平时与IOS和安卓的同学同事调试,大致归纳了有以下四种:
RCTDeviceEventEmitter 事件方式
Callback 回调方式
Promise 信任方式
直传常量数据
先比较下优缺点:eventMitter的形式可任意时刻传递,由Native主导控制,callback形式由JS调用Native返回,但是是异步的时机不确定,所以有Promise形式,但是要不断的JS调用如轮询。直传常量跨域传值,只能从原生端向RN端传递。RN端可通过 NativeModules.[module名].[参数名] 的方式获取
了解了三者的通信方式,怎么能少了代码的描述!我们来看看代码如何实现。大致的实现步骤如下:
以下是安卓代码的编写 eventMitter
1. 定义Module类,继承ReactContextBaseJavaModule, 在Module类中,我们定义交互的方法,例如RN调用Native的方法,Native调用RN的方法等。
@Override
public String getName() {
return MODULE_NAME;
}
/**
* RN调用Native的方法在module中定义一个方法,并用@ReactMethod 注解标注:表明该方法会被RN调用。即被RN调用的原生方法必须使用@ReactMethod注解标注。
*/
@ReactMethod
public void rnCallNative(String a) {
// XXX.
mContext.startActivity(XX);
}
/**
* Native调用RN 上面代码定义了原生方法,通过在Android层调用RN层。使用ReactContext的getJSModule方法,emit来发送消息。同样,emit的第一个参数要与RN层中addListener方法的第一个参数相同。
* @param msg
*/
public void nativeCallRn(String msg) { mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit( EVENT_NAME,msg);
}
2. 定义Package类,继承ReactPackage实现Package的createNativeModules方法,将Module实例添加到集合。
/** 通信Package类 */
public class CommPackage implements ReactPackage {
public CommModule mModule;
/**
* 创建Native Module 在createNativeModules方法中,初始化集合,并将module实例添加进集合,返回集合实例。
* @param reactContext
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
mModule = new CommModule(reactContext);
modules.add(mModule);
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
3. 定义Application,继承ReactApplication实现getPackages方法,将Package实例添加到getPackages下的集合。
private static final CommPackage mCommPackage = new CommPackage();
**
* 获取 reactPackage getPackages方法中,将Package实例添加到Arrays中即可完成注册
*/
public static CommPackage getReactPackage() {
return mCommPackage;
}
以下是RN代码的编写,通过 RCTDeviceEventEmitter 模式进行通信交互。交互都是以主动方式为主
1. 调用原生代码
/**
* 调用原生代码 在React Native层,通过NativeModules调用commModule
*/
nativeCall() {
NativeModules.commModule.rnCallNative(xxx);
}
2. 接收原生调用
/**
* 接收原生调用通过DeviceEventEmitter注册监听,类似于Android中的监听事件。第一个参数标识名称,要与Module中emit的Event Name相同。第二个参数即为处理回掉IOS应该为NativeEventEmitter
*/
componentDidMount() {
DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{ });
}
Callback形式 RN层调用Native层,Native层处理完成后,回调RN层
**
* Callback 方式
* rn调用Native,并获取返回值
*/
@ReactMethod
public void rnCallNativeCb(String msg, Callback callback) {
callback.invoke(xxx); // 2.回调RN,即将处理结果返回给RN
}
// RN
callbackComm(msg) {
NativeModules.commModule.rnCallNativeCb(msg,(result) => {})
}
Promise形式 同样是RN层调用Native层,Native层处理完成后,回调RN层
@ReactMethod
public void rnCallNativePromise(String msg, Promise promise) {
promise.resolve(xxx);
}
// RN
promiseComm(msg) {
NativeModules.commModule.rnCallNativePromise(msg).then((result) =>{}).catch((error) => {console.log(error)});
}
直传常量数据(原生向RN)
@Nullable
@Override
@return a map of constants this module exports to JS. Supports JSON types.
从源码注释中可以看出,该方法是返回一个Map类型的常量,导出到JS端(即RN)。支持JSON 类型。所以,我们只需要重写方法,声明Map集合,向其中添加常量后,返回即可
public Map<String, Object> getConstants() {
return super.getConstants();
}
// Ours Android
@Nullable
@Override
public Map<String, Object> getConstants() {
Map<String,Object> params = new HashMap<>();
params.put("Constantxx","xxx");
return params;
}
// Ours RN
componentWillMount() {
let result = NativeModules.MyModule.Constantxx
}