天问

  • Blog
  • 热修复与动态加载
  • python爬虫
  • 专栏
    • 热修复与动态加载
    • NDK开发
    • 设计模式与架构
    • React Native
  • 好文转载
    • 计算机技术
    • 投资理财
    • 摄影与拍照技巧
  • 投稿
  • 关于本站及隐私政策
  • Github
StevenQ
为今天工作,为明天投资,为未来孵化一些东西!
  1. 首页
  2. 计算机技术
  3. 安卓开发
  4. 正文

【React Native】js端调用安卓原生模块及2端互相通信

2018年9月22日 1184点热度 0人点赞 0条评论

前面React Native编译常见问题解决和官方demo分析一文介绍了FaceBook的RN官方demo,初步介绍了官方demo的一些重要的模块及其作用。这一节就来讲解下如何在js端调用安卓原生模块,以及js端和安卓原生模块之间的互相通信的几种方式和他们的特点。因为对于任何一个跨语言的开发框架来讲,在开发过程中2端之间的相互调用和消息传递是非常基础和也是非常重要的。就像NDK开发(java和c/c++互相调用)和WebView使用一样(java和js互相调用)。

在JS端调用安卓原生模块

这里以改造官方demo为例,因为自己接触RN是因为我们的验证码SDK有个客户希望能给一个RN的demo,所以这里以在js代码中点击登陆按钮调用易盾验证码SDK为例进行讲解。期望达到的效果是:

  • 点击js代码中的登陆按钮,显示验证码界面(js端调用安卓原生模块)
  • 滑动验证码验证通过,js端弹出一个“验证成功”提示框,反之弹出“验证失败”提示框(安卓端与js端通信)

这个demo能够很好的把js端调用安卓原生模块(验证码SDK)和安卓端向js端传递消息(告知验证是否通过)包含进来。打个广告,欢迎大家使用网易易盾验证码SDK服务进行行为式验证,告别繁琐的输入字符验证:http://dun.163.com/trial/jigsaw

首先修改demo的App.js。添加一个按钮以及监听事件。

export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit App.js
        </Text>
        <Text style={styles.instructions}>
          {instructions}
        </Text>
        //增加一个登陆按钮
		<Button  
		onPress={onButtonPress}
		title="登录"
		color="#841584"
		/>
      </View>
    );
  }
}
//定义按钮的响应事件,在该函数调用安卓端的验证码SDK
const onButtonPress = () => {
  //Alert.alert('Button has been pressed!');
};

在登陆按钮的事件响应函数中调用安卓端的SDK,所以接下来就是在安卓端创建要被js调用的原生模块。

在安卓端创建要被js调用的原生模块

  1. 创建一个原生模块类继承自ReactContextBaseJavaModule抽象类,实现getName方法,返回该原生模块的模块名。(该方法的返回值即为需要在JS代码中引入的模块名)
  2. 在需要被JS代码调用的方法处加上@ReactMethod注解
public class CaptchaModule extends ReactContextBaseJavaModule {
    private CaptchaHelper captchaHelper = null;

    public CaptchaModule(ReactApplicationContext reactContext) {
        super(reactContext);
        captchaHelper = new CaptchaHelper();
    }


    @Override
    public String getName() {
        return "CaptchaHelper"; //返回需要在js端被引入的模块名
    }

    @ReactMethod  //加上该注解,表面该方法时一个RN方法,可以被js端调用
    public void showCaptcha() {
        captchaHelper.init(getCurrentActivity());
        captchaHelper.show();
    }
}
  1. 创建一个包层次的模块管理类实现ReactPackage接口,实现该接口的2个方法。在createNativeModules方法中将前面创建的模块类添加到包模块管理List中
public class CaptchaPackageManager implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new CaptchaModule(reactContext));//添加前面创建的验证码模块类
        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}
  1. 在demo的MainApplication的相应位置将前面创建的包管理模块添加进去
public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(), new CaptchaPackageManager()//添加包管理模块
            );
        }

        @Override
        protected String getJSMainModuleName() {
            return "index";
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }
}

之间的层次关系可以这么理解:

  • 继承自ReactContextBaseJavaModule的原生模块相当于java端的需要被js端调用的类模块,在这个模块中返回该模块希望被js端调用的类模块名(实现getName方法)和希望被js端调用的函数(在函数上面加上@ReactMethod注解)
  • 实现了ReactPackage的原生模块相当于java端的包层次模块,在这个模块中添加前面创建的类模块
  • 在MainApplication内部类ReactNativeHost的getPackages方法中添加前面创建的包管理模块

js端调用安卓端创建的类模块

  1. 引入原生模块NativeModules
import {
  Platform,
  StyleSheet,
  Text,
  View,
  Alert,
  Button,
  NativeModules,//引入RN的原生模块
} from 'react-native';
  1. 调用安卓端相应模块的RN方法
const onButtonPress =  () => {
	NativeModules.CaptchaHelper.showCaptcha(); //CaptchaHelper是在安卓端注册的模块,showCaptcha是安卓端添加注解的RN方法
}

2端之间的相互通信

[wp_table id=826/]

2端之间的消息传递方式可以概括为以上3种方式,我们一个一个来看。

回调Callback

  • 在安卓端需要被JS调用的ReactMethod中增加Callback这个对象参数,在需要发送消息的地方使用Callback的invoke方法传递消息。该函数定义如下:
public void invoke(Object... args);

也就是该方法的参数是一个对象数组。因此如果我们有多个消息要传递,直接把多个消息对象作为参数传入调用即可。

@ReactMethod
public void showCaptcha(Callback callback) {//在RN方法中增加Callback参数
       captchaHelper.init(getCurrentActivity(),callback);
       captchaHelper.show();
}
  • 在需要发送消息的地方调用invoke方法
public void onValidate(String result, String validate, String message) {
        //验证结果,valiadte,可以根据返回的三个值进行用户自定义二次验证
        if (validate.length() > 0) {
            mCallback.invoke("success");
        } else {
            mCallback.invoke("error");
        }
  • 在JS端调用时传入回调函数接收回调消息.
const onButtonPress =  () => {
	NativeModules.CaptchaHelper.showCaptcha(msg=>{ //调用安卓原生模块时传入回调函数
		if(msg== "success"){
		    Alert.alert("提示","验证通过");
		}else{
		    Alert.alert("提示","验证失败");
		}
	});
};

回调函数方式,JS端是主动调用方,安卓端是被动消息发送方。对于验证码验证而言,安卓端可能会多次触发验证结果,即多次调用invoke方法。但是因为JS端只调用了一次原生模块函数。所以只会回调一次。因此对于验证码而言,不大合适使用回调方式从安卓端向js端传递消息。

发送事件

在JS端建立事件监听函数,在安卓端调用RN提供的事件发送API发送事件。

安卓端代码

private void sendMsgToJs(String msg) {
        ((MainActivity)mContext).getReactContext()
        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
        .emit("onValidate", msg); //对onValidate事件发送msg消息
}

js端代码

componentWillMount(){  
        //监听事件名为onValidate的事件
        DeviceEventEmitter.addListener('onValidate', function(msg) {  
		if(msg== "success"){
			Alert.alert("提示","验证通过");
		}else{
			Alert.alert("提示","验证失败");
		}
	});
}

事件发送方式安卓端是主动方,js端是被动监听方,所以可以进行多次消息传递。而且这种方式无需作为RN函数的参数,所以代码耦合性低。

Promises

将安卓原生模块声明的RN方法的最后一个参数定义为Promise对象,则在JS端调用该RN方法时会返回一个Promise对象。在JS端调用该方法的时候一般会配合ES7标准的async/await语法。安卓端在需要发送消息的地方使用Promise的resolve方法传递消息。关于Promise的介绍可以参看:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

安卓端代码
在RN方法的最后一个参数处添加Promise参数

@ReactMethod
public void showCaptcha(Promise promise) {//最后一个参数需要为Promise对象
     captchaHelper.init(getCurrentActivity(),promise);
     captchaHelper.show();
}

在需要发送消息的地方使用Promise的resolve方法传递消息

public void onValidate(String result, String validate, String message) {
        //验证结果,valiadte,可以根据返回的三个值进行用户自定义二次验证
        if (validate.length() > 0) {
            mPromise.resolve("success");
        } else {
            mPromise.resolve("failed");
        }
    }

js端代码
调用RN函数,该函数会返回一个Promise对象,从该对象中得到安卓端发送过来的消息

const onButtonPress = async () => {
	var msg= await NativeModules.CaptchaHelper.showCaptcha();//配合await否则会直接执行后面语句,而且await必须和async同时使用
	if(msg== "success"){
		Alert.alert("提示","验证通过");
	}else{
		Alert.alert("提示","验证失败");
	}
};


最终demo效果及源码

 

 

 

 

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: React-Native
最后更新:2018年9月30日

天问

追梦追自由,穷则独善其身,达则兼济天下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022 天问 ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang