ReactNative与原生Android通信交互

前言

之前对ReactNative有过研究,恰好近期公司想在节约成本的前提下,进一步提升App的使用体验,相比起某些页面嵌入H5页面来说,RN的体验更接近原生,加上RN发展这么久生态已经比较庞大,于是决定将RN加入项目实践中。


分析

为了实现React Native与原生App之间的通信,FB实现了自己的一套交互机制,分别是:

  • RCTDeviceEventEmitter 事件方式
  • Callback 回调方式
  • Promise 异步方式

三种方式各具不同优缺点:

1.RCTDeviceEventEmitter

优点:可任意时刻传递,Native主导控制。

2.Callback

优点:JS调用一次,Native返回。

缺点:CallBack为异步操作,返回时机不确定

3.Promise

优点:JS调用一次,Native返回。

缺点:每次使用需要JS调用一次


原生模块

实现三种通信方式前,我们首先去实现原生模块:

第一步:

一个原生模块是一个继承了ReactContextBaseJavaModule的 Java 类,它可以实现一些 JavaScript 所需的功能。

创建一个新的 Java 类并命名为TransMissonMoudle.java,其代码如下:

public class TransMissonMoudle extends ReactContextBaseJavaModule {

    private static final String REACT_CLASS = "TransMissonMoudle";
    private ReactContext mReactContext;

    public TransMissonMoudle(ReactApplicationContext reactContext) {
        super(reactContext);
        this.mReactContext = reactContext;
    }

    @Override
    public String getName() {
        return REACT_CLASS;
    }
}

ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在 JavaScript 端标记这个模块。这里我们把这个模块叫做TransMissonMoudle,这样就可以在 JavaScript 中通过NativeModules.TransMissonMoudle访问到这个模块。

第二步:

注册这个模块,我们需要在应用的 Package 类的createNativeModules方法中添加这个模块。如果模块没有被注册,它也无法在 JavaScript 中被访问到。

创建一个新的 Java 类并命名为TransMissonPackage.java,其代码如下:

public class TransMissonPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new TransMissonMoudle(reactContext));

        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();

    }
}

第三步:

这个 package 需要在MainApplication.java文件的getPackages方法中提供

@Override
protected List<ReactPackage> getPackages() {
  return Arrays.<ReactPackage>asList(
      new MainReactPackage(),
      new TransMissonPackage()// <-- 添加这一行,类名替换成你的Package类的名字.
  );
}

现在,在别处的 JavaScript 代码中可以这样调用你的方法(本地方法被@ReactMethod才能直接调用),具体的方法我们后面实现:

import { NativeModules } from 'react-native';
NativeModules.TransMissonMoudle.方法

通信交互

1.RCTDeviceEventEmitter

原生模块可以在没有被调用的情况下往 JavaScript 发送事件通知。最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获得对应的引用,像这样:

/**
 * 1.RCTDeviceEventEmitter方式
 * @param reactContext
 * @param eventName    事件名
 * @param params       传参
 */
public void sendTransMisson(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
    reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName, params);
}

@ReactMethod
public void contactWithRTC(String type) {
    WritableMap writableMap = new WritableNativeMap();
    switch (type){
        case "getTime":
            writableMap.putString("type",type);
            writableMap.putString("age","10");
            writableMap.putString("time", getTimeMillis());
            break;
        default:
            break;
    }

    sendTransMisson(mReactContext, "rnTransMisson", writableMap);
}

JavaScript 模块可以通过使用DeviceEventEmitter模块来监听事件:

import { DeviceEventEmitter } from 'react-native';

componentWillMount() {
  DeviceEventEmitter.addListener('rnTransMisson', (e: Event) => {
       this.refs.toast.show("DeviceEventEmitter收到消息:" + "\n" + "年龄:" + msg.age + "时间:" + msg.time, 2000);
  });
}

NativeModules.TransMissonMoudle.contactWithRTC("getTime");

2.Callback 回调方式

原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给 JavaScript。

原生模块通常只应调用回调函数一次。但是,它可以保存 callback 并在将来调用。

请务必注意 callback 并非在对应的原生函数返回后立即被执行——注意跨语言通讯是异步的,这个执行过程会通过消息循环来进行。

/**
 * 2.CallBack方式
 * @param type
 * @param callback
 */
@ReactMethod
public void contactWithCallBack(String type, Callback callback) {
    switch (type){
        case "getTime":
            callback.invoke(getTimeMillis());
            break;
        default:
            break;
    }
}

这个函数可以在 JavaScript 里这样使用:

NativeModules.TransMissonMoudle.contactWithCallBack("getTime",
    (msg) => {
        this.refs.toast.show("CallBack收到消息:" + "\n" + msg, 2000);
    }
);

3.Promise

原生模块还可以使用 promise 来简化代码,搭配 ES2016(ES7)标准的async/await语法则效果更佳。如果桥接原生方法的最后一个参数是一个Promise,则对应的 JS 方法就会返回一个 Promise 对象。

我们把上面的代码用 promise 来代替回调进行重构:

/**
 * 3.Promise方式
 * @param type
 * @param promise
 */
@ReactMethod
public void contactWithPromise(String type, Promise promise) {
    switch (type) {
        case "getTime":

            WritableMap writableMap=new WritableNativeMap();
            writableMap.putString("age","30");
            writableMap.putString("time",getTimeMillis());
            promise.resolve(writableMap);

            break;
        default:
            break;
    }
}

现在 JavaScript 端的方法会返回一个 Promise。这样你就可以在一个声明了async的异步函数内使用await关键字来调用,并等待其结果返回。(虽然这样写着看起来像同步操作,但实际仍然是异步的,并不会阻塞执行来等待)。

NativeModules.TransMissonMoudle.contactWithPromise("getTime")
    .then(msg=> {
        this.refs.toast.show("Promise收到消息:" + "\n" + "年龄:" + msg.age + "时间:" + msg.time, 2000);
        
    }).catch(error=> {
        console.log(error);
    });

至此,三种通信方式已经介绍完了。

按照惯例,上源码。

https://github.com/tolvgx/RNTransMisson

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值