[置顶] React Native与Android通信交互
2017-02-17 11:56
459 查看
刚创建的React Native 微信公众号,欢迎微信扫描关注订阅号,每天定期会分享react
native 技术文章,移动技术干货,精彩文章技术推送。同时可以扫描我的微信加入react-native技术交流微信群。欢迎各位大牛,React
Native技术爱好者加入交流!
在前两篇的内容中,和大家分享了Android原生集成RN,以及RN的增量热更新。关于详细的内容,点击如下具体了解:
Android App巧妙集成React Native最详教程
React Native 实现热部署、增量热更新
本篇内容同样和React Native 与 原生App有关,可以说更加深入了两者之间的感情,为培养下一代做出准备:React Native与原生App的通信交互。
Android系统为我们提供了webview来加载网页,为了让webview加载的网页可以与App交互,系统提供了一套机制帮助我们更方便的实现通信。同样为了实现React Native与原生App之间的通信,FB也实现了自己的一套交互机制。
(1)RCTDeviceEventEmitter 事件方式
(2)Callback 回调方式
(3)Promise
(4)直传常量数据(原生向RN)
三种方式各具不同优缺点
1. RCTDeviceEventEmitter
优点:可任意时刻传递,Native主导控制。
2. Callback
优点:JS调用,Native返回。
缺点:CallBack为异步操作,返回时机不确定
3. Promise
优点:JS调用,Native返回。
缺点:每次使用需要JS调用一次
了解了三者的通信方式,怎么能少了代码的描述!我们来看看代码如何实现。大致的实现步骤如下:
(1)定义Module类,继承ReactContextBaseJavaModule
在Module类中,我们定义交互的方法,例如RN调用Native的方法,Native调用RN的方法等。
(2)定义Package类,继承ReactPackage
实现Package的createNativeModules方法,将Module实例添加到集合。
(3)定义Application,继承ReactApplication
实现getPackages方法,将Package实例添加到getPackages下的集合。
4. 直传常量数据(原生向RN)
跨域传值,只能从原生端向RN端传递。RN端可通过 NativeModules.[module名].[参数名] 的方式获取
1.Module类中的核心代码
名称可以自定义,对接时协商好即可。
在module中定义一个方法,并用@ReactMethod 注解标注:表明该方法会被RN调用。即被RN调用的原生方法必须使用@ReactMethod注解标注。
注意:RN层调用Native层进行界面跳转时,需要设置FLAG_ACTIVITY_NEW_TASK标志,否则会出现如下错误:
上面代码定义了原生方法,通过在Android层调用RN层。使用ReactContext的getJSModule方法,emit来发送消息。同样,emit的第一个参数要与RN层中addListener方法的第一个参数相同。
2.自定义Package的核心代码
在createNativeModules方法中,初始化集合,并将module实例添加进集合,返回集合实例。
3.Application核心代码
在getPackages方法中,将Package实例添加到Arrays中即可完成注册。以上就是Android层核心代码配置,继续来看React Native层核心代码:
1.调用原生代码
在React Native层,通过NativeModules调用commModule,继而调用原生方法即可。注意:commModule要与Module类的getNames方法返回的名称对应。
2. 接收原生调用
通过DeviceEventEmitter注册监听,类似于Android中的监听事件。第一个参数标识名称,要与Module中emit的Event Name相同。第二个参数即为处理回掉。
3.界面代码
在Text中注册单击事件,RN层调用原生代码,跳转到拨号界面。
4.Android层调用RN的代码
调用Module中定义的nativeCallRn方法,继而出发RN层代码。以上就是通过 RCTDeviceEventEmitter 模式进行通信交互。可以很清晰的看出,交互都是以主动方式为主。
RN中剩下的两种通信方式,存在一个共同的特点:
从RN层调用Native层,Native层处理完成后,回调RN层
直接看代码实现:
(1)Callback
同样还是在Module类中定义交互方法:
RN中定义回调:
(2)Promise
Module类中定义交互方法:
RN中定义回调:
布局中触发单击事件:
(3)直传常量数据(原生向RN)
自定义Module类中实现getConstants方法
最终效果:
Android调用React Native:
React Native调用Android:
源码以上传到github,希望大家star,follow支持哦~
源码下载
native 技术文章,移动技术干货,精彩文章技术推送。同时可以扫描我的微信加入react-native技术交流微信群。欢迎各位大牛,React
Native技术爱好者加入交流!
在前两篇的内容中,和大家分享了Android原生集成RN,以及RN的增量热更新。关于详细的内容,点击如下具体了解:
Android App巧妙集成React Native最详教程
React Native 实现热部署、增量热更新
本篇内容同样和React Native 与 原生App有关,可以说更加深入了两者之间的感情,为培养下一代做出准备:React Native与原生App的通信交互。
Android系统为我们提供了webview来加载网页,为了让webview加载的网页可以与App交互,系统提供了一套机制帮助我们更方便的实现通信。同样为了实现React Native与原生App之间的通信,FB也实现了自己的一套交互机制。
(1)RCTDeviceEventEmitter 事件方式
(2)Callback 回调方式
(3)Promise
(4)直传常量数据(原生向RN)
三种方式各具不同优缺点
1. RCTDeviceEventEmitter
优点:可任意时刻传递,Native主导控制。
2. Callback
优点:JS调用,Native返回。
缺点:CallBack为异步操作,返回时机不确定
3. Promise
优点:JS调用,Native返回。
缺点:每次使用需要JS调用一次
了解了三者的通信方式,怎么能少了代码的描述!我们来看看代码如何实现。大致的实现步骤如下:
(1)定义Module类,继承ReactContextBaseJavaModule
在Module类中,我们定义交互的方法,例如RN调用Native的方法,Native调用RN的方法等。
(2)定义Package类,继承ReactPackage
实现Package的createNativeModules方法,将Module实例添加到集合。
(3)定义Application,继承ReactApplication
实现getPackages方法,将Package实例添加到getPackages下的集合。
4. 直传常量数据(原生向RN)
跨域传值,只能从原生端向RN端传递。RN端可通过 NativeModules.[module名].[参数名] 的方式获取
1.Module类中的核心代码
/** * 在rn代码里面是需要这个名字来调用该类的方法 * @return */ @Override public String getName() { return MODULE_NAME; }
名称可以自定义,对接时协商好即可。
/** * RN调用Native的方法 * @param phone */ @ReactMethod public void rnCallNative(String phone) { // 跳转到打电话界面 Intent intent = new Intent(); intent.setAction(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:" + phone)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 跳转需要添加flag, 否则报错 mContext.startActivity(intent); }
在module中定义一个方法,并用@ReactMethod 注解标注:表明该方法会被RN调用。即被RN调用的原生方法必须使用@ReactMethod注解标注。
注意:RN层调用Native层进行界面跳转时,需要设置FLAG_ACTIVITY_NEW_TASK标志,否则会出现如下错误:
/** * Native调用RN * @param msg */ public void nativeCallRn(String msg) { mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(EVENT_NAME,msg); }
上面代码定义了原生方法,通过在Android层调用RN层。使用ReactContext的getJSModule方法,emit来发送消息。同样,emit的第一个参数要与RN层中addListener方法的第一个参数相同。
2.自定义Package的核心代码
/** * 通信Package类 * Created by Song on 2017/2/17. */ public class CommPackage implements ReactPackage { public CommModule mModule; /** * 创建Native Module * @param reactContext * @return */ @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(); } }
在createNativeModules方法中,初始化集合,并将module实例添加进集合,返回集合实例。
3.Application核心代码
private static final CommPackage mCommPackage = new CommPackage();
/** * 获取 reactPackage * @return */ public static CommPackage getReactPackage() { return mCommPackage; }
在getPackages方法中,将Package实例添加到Arrays中即可完成注册。以上就是Android层核心代码配置,继续来看React Native层核心代码:
1.调用原生代码
/** * 调用原生代码 */ skipNativeCall() { let phone = '18637070949'; NativeModules.commModule.rnCallNative(phone); }
在React Native层,通过NativeModules调用commModule,继而调用原生方法即可。注意:commModule要与Module类的getNames方法返回的名称对应。
2. 接收原生调用
/** * 接收原生调用 */ componentDidMount() { DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{ title = "React Native界面,收到数据:" + msg; ToastAndroid.show("发送成功", ToastAndroid.SHORT); }) }
通过DeviceEventEmitter注册监听,类似于Android中的监听事件。第一个参数标识名称,要与Module中emit的Event Name相同。第二个参数即为处理回掉。
3.界面代码
render() { return ( <View style={styles.container}> <Text style={styles.welcome} > {title} </Text> <Text style={styles.welcome} onPress={this.skipNativeCall.bind(this)}> 跳转到拨号界面 </Text> <Image source={require('./images/ic.png')} /> </View> ); }
在Text中注册单击事件,RN层调用原生代码,跳转到拨号界面。
4.Android层调用RN的代码
/** * 向RN发送消息 * @param v */ public void sendMsgToRN(View v) { Log.e("---","sendMsgToRN"); MainApplication.getReactPackage().mModule.nativeCallRn("hello"); }
调用Module中定义的nativeCallRn方法,继而出发RN层代码。以上就是通过 RCTDeviceEventEmitter 模式进行通信交互。可以很清晰的看出,交互都是以主动方式为主。
RN中剩下的两种通信方式,存在一个共同的特点:
从RN层调用Native层,Native层处理完成后,回调RN层
直接看代码实现:
(1)Callback
同样还是在Module类中定义交互方法:
/** * Callback 方式 * rn调用Native,并获取返回值 * @param msg * @param callback */ @ReactMethod public void rnCallNativeFromCallback(String msg, Callback callback) { // 1.处理业务逻辑... String result = "处理结果:" + msg; // 2.回调RN,即将处理结果返回给RN callback.invoke(result); }
RN中定义回调:
/** * Callback 通信方式 */ callbackComm(msg) { NativeModules.commModule.rnCallNativeFromCallback(msg,(result) => { ToastAndroid.show("CallBack收到消息:" + result, ToastAndroid.SHORT); }) }
(2)Promise
Module类中定义交互方法:
/** * Promise 方式 * @param msg * @param promise */ @ReactMethod public void rnCallNativeFromPromise(String msg, Promise promise) { Log.e("---","adasdasda"); // 1.处理业务逻辑... String result = "处理结果:" + msg; // 2.回调RN,即将处理结果返回给RN promise.resolve(result); }
RN中定义回调:
/** * Promise 通信方式 */ promiseComm(msg) { NativeModules.commModule.rnCallNativeFromPromise(msg).then( (result) =>{ ToastAndroid.show("Promise收到消息:" + result, ToastAndroid.SHORT) } ).catch((error) =>{console.log(error)}); }
布局中触发单击事件:
<Text style={styles.welcome} onPress={this.skipNativeCall.bind(this)}> 跳转到拨号界面 </Text> <Text style={styles.welcome} onPress={this.callbackComm.bind(this,'callback发送啦')}> Callback通信方式 </Text> <Text style={styles.welcome} onPress={this.promiseComm.bind(this,'promise发送啦')}> Promise通信方式 </Text>
(3)直传常量数据(原生向RN)
自定义Module类中实现getConstants方法
@Nullable @Override public Map<String, Object> getConstants() { return super.getConstants(); }以上是默认实现,getConstants方法中调用了super.getConstants(),跟踪源码可以看到
/** * @return a map of constants this module exports to JS. Supports JSON types. */ public @Nullable Map<String, Object> getConstants() { return null; }从源码注释中可以看出,该方法是返回一个Map类型的常量,导出到JS端(即RN)。支持JSON 类型。所以,我们只需要重写方法,声明Map集合,向其中添加常量后,返回即可。所以就有了如下代码
@Nullable @Override public Map<String, Object> getConstants() { Map<String,Object> params = new HashMap<>(); params.put("Constant","我是常量,传递给RN"); return params; }此时,在RN端,我们可以通过NativeModules来接收即可
componentWillMount() { let result = NativeModules.MyModule.Constant }
最终效果:
Android调用React Native:
React Native调用Android:
源码以上传到github,希望大家star,follow支持哦~
源码下载
相关文章推荐
- [置顶] React Native 移动技术在企业架构的应用
- [置顶] 基于React Native的移动平台研发实践分享
- [置顶] React Native 如何区分系统平台,动态加载组件
- [置顶] 【稀饭】react native 实战系列教程之热更新原理分析与实现
- [置顶] React Native
- [置顶] React Native 网络请求封装:使用Promise封装fetch请求
- [置顶] React Native项目组织结构介绍
- [置顶] 使用React Native 和 微信小程序 编写的一款阅读类app ———《轻松一刻》
- [置顶] React Native开源项目 「漫画书」
- [置顶] [深入剖析React Native总贴]React Native学习路线
- [置顶] React Native运行原理解析
- [置顶] [深入剖析React Native总贴]React Native学习路线
- [置顶] React Native未来导航者:react-navigation 使用详解(进阶篇)
- [置顶] 在windows上搭建React Native开发环境时遇到licence问题的解决
- [置顶] react native中由于undefined与null引发的血案
- [置顶] React Native实现一个自定义模块
- [置顶] React Native基于最新版本实现JsBundle预加载,解决白屏等待,界面秒开优化
- [置顶] React Native开发错误警告处理总结(持续更新)
- [置顶] Android原生项目集成React Native
- [置顶] react Native 一:在ubuntu MATE 16.04 下搭建android studio开发环境