【简述RN集成到Android原生项目】
【Android项目集成RN系列:RN使用Android原生控件或自定义组件】
【React Native Linking与 Android原生页面路由跳转问题】

Android与RN通信.png

React Native在Android混编项目中的页面跳转和方法调用大致可以通过上面这张简图来描述下:

一、页面跳转(RN与Android原生)

  1. RN页面跳转原生

    • 方式一:
      通过下面即将讲述的方法调用实现,通过在RN中调用 NativeModule中暴露的方法,来实现跳转原生的指定页面。(此处不再细述)
    • 方式二:
      通过Scheme路由形式跳转,RN提供的Linking,可根据路由跳转到指定页面,可参考【React Native Linking与 Android原生页面路由跳转问题】。
  2. 原生跳转RN页面
    通常我们加载RN页面是根据RN bundle文件的入口js文件(默认index.android.js,可自定义指定入口js文件)中暴露的ModuleName去加载入口页面,就好比我们Android中设置的启动页面一样,我们再在启动页面根据相关逻辑跳转相关页面。那么这里可能就涉及到了Android如何传递路由或者参数给RN?以及RN如何获取原生提供的路由与参数?通过查看Linking源码发现RN分别针对Android和IOS进行了封装映射,我们只需要把数据传送到对应的位置即可,

    const LinkingManager =Platform.OS === 'android'? NativeModules.IntentAndroid : NativeModules.LinkingManager;
    

    在Android中对应的是IntentAndroid,查看对应的源码:

    /*** Return the URL the activity was started with*
    * @param promise a promise which is resolved with the initial URL
    */@ReactMethodpublic void getInitialURL(Promise promise) {try {Activity currentActivity = getCurrentActivity();String initialURL = null;if (currentActivity != null) {Intent intent = currentActivity.getIntent();String action = intent.getAction();Uri uri = intent.getData();if (Intent.ACTION_VIEW.equals(action) && uri != null) {initialURL = uri.toString();}}promise.resolve(initialURL);} catch (Exception e) {promise.reject(new JSApplicationIllegalArgumentException("Could not get the initial URL : " + e.getMessage()));}}
    

    通过上面源码也就了解了Android与RN如何根据Linking进行页面跳转,我先按照这种形式在Android上进行测试发现可行,当然也有bug,就是拿的时候可能为空,调查发现RN与原生页面装载之前就调用这个方法导致拿不到上下文,只要创建rootview就会执行rn的生命周期,而此时RN与原生还没有绑定,后来发现RN是能监听到原生页面的活动状态,此时再去获取路由即可。详细内容可参考React Native Linking与 Android原生页面路由跳转实现

二、 方法调用

RN通信原理简单地讲就是,一方native(java)将其部分方法注册成一个映射表,另一方(js)再在这个映射表中查找并调用相应的方法,而Bridge担当两者间桥接的角色。
其实方法调用大致分为2种情况:

  • Android主动向JS端传递事件、数据
  • JS端主动向Android询问获取事件、数据

RN调用Android需要module名和方法名相同,而Android调用RN只需要方法名相同。
(1)RCTDeviceEventEmitter 事件方式
​ 优点:可任意时刻传递,Native主导控制。
(2)Callback 回调方式
​ 优点:JS调用,Native返回。
​ 缺点:CallBack为异步操作,返回时机不确定
(3)Promise
​ 优点:JS调用,Native返回。
​ 缺点:每次使用需要JS调用一次
(4)直传常量数据(原生向RN)
​ 跨域传值,只能从原生端向RN端传递。RN端可通过 NativeModules.[module名].[参数名] 的方式获取。

注意:RN层调用NativeModule层进行界面跳转时,需要设置FLAG_ACTIVITY_NEW_TASK标志。

例如下分别以原生调用RNRN调用原生为例简单描述下:

  1. 原生调用RN
    下面是RCTDeviceEventEmitter事件的简单事例,稍后封装下更方便与原生的通信交互。

    public class EventEmitter {private static final String TAG = "EventEmitter";
    // 在ReactPackage中的createNativeModules()初始化,RNEventEmitter.setReactContext(reactContext);private static ReactApplicationContext mReactContext;public static void setReactContext(ReactApplicationContext mReactContext) {RNEventEmitter.mReactContext = mReactContext;}/*** 显示RN中loading* @param data show:显示,false:隐藏*/public static void showLoading(boolean show) {sendEventToRn("showloading", show);}public static void sendEventToRn(String eventName, Object msg) {if (mReactContext == null) {Log.e(TAG, "ReactContext is null");return;}mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,msg);}
    }
    

    RN中接收原生消息:

     /*** 接收原生调用*/componentDidMount() {DeviceEventEmitter.addListener('showLoading',(msg)=>{ToastAndroid.show("发送成功"+msg, ToastAndroid.SHORT);})//通过DeviceEventEmitter注册监听,类似于Android中的监听事件。第一个参数标识名称,要与Module中emit的Event Name相同。第二个参数即为处理回调时间。}
    
  2. RN调用原生
  • 创建MyNativeModule.java

    package com.liujc.rnappdemo.hybride;import android.app.Activity;
    import android.content.Intent;
    import android.support.annotation.Nullable;
    import android.text.TextUtils;
    import android.util.Log;
    import android.widget.Toast;import com.facebook.react.bridge.Callback;
    import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
    import com.facebook.react.bridge.Promise;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import com.facebook.react.modules.core.DeviceEventManagerModule;import java.util.HashMap;
    import java.util.Map;/*** 类名称:MyNativeModule* 创建者:Create by liujc* 描述:原生预留给RN的调用方法*/
    public class MyNativeModule extends ReactContextBaseJavaModule {public static final String REACT_NATIVE_CLASSNAME = "MyNativeModule";private ReactApplicationContext mContext;public static final String EVENT_NAME = "nativeCallRn";public static final String TAG = "TAG";public MyNativeModule(ReactApplicationContext reactContext) {super(reactContext);mContext = reactContext;}@Overridepublic String getName() {//返回的这个名字是必须的,在rn代码中需要这个名字来调用该类的方法。return REACT_NATIVE_CLASSNAME;}//函数不能有返回值,因为被调用的原生代码是异步的,原生代码执行结束之后只能通过回调函数或者发送信息给rn那边。@ReactMethodpublic void rnCallNative(String msg){Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show();}@ReactMethodpublic void startActivityRN(String name, String params) {try {Activity activity = getCurrentActivity();if (activity != null) {Class toClass = Class.forName(name);Intent intent = new Intent(activity, toClass);intent.putExtra("params", params);activity.startActivity(intent);}} catch (Exception ex) {throw new JSApplicationIllegalArgumentException("不能打开Activity " + ex.getMessage());}}//后面该方法可以用Linking代替@ReactMethod  public void getDataFromIntent(Callback success, Callback error) {try {Activity currentActivity = getCurrentActivity();String result = currentActivity.getIntent().getStringExtra("result");//会有对应数据放入if (!TextUtils.isEmpty(result)) {success.invoke(result);}} catch (Exception ex) {error.invoke(ex.getMessage());}}/*** Native调用RN* @param msg*/public void nativeCallRn(String msg) {          mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(EVENT_NAME,msg);}/*** Callback 方式* rn调用Native,并获取返回值* @param msg* @param callback*/@ReactMethodpublic void rnCallNativeFromCallback(String msg, Callback callback) {String result = "hello RN!Native正在处理你的callback请求";// .回调RN,即将处理结果返回给RNcallback.invoke(result);}/*** Promise* @param msg* @param promise*/@ReactMethodpublic void rnCallNativeFromPromise(String msg, Promise promise) {Log.e(TAG,"rnCallNativeFromPromise");String result = "hello RN!Native正在处理你的promise请求" ;promise.resolve(result);}/*** 向RN传递常量*/@Nullable@Overridepublic Map<String, Object> getConstants() {Map<String,Object> params = new HashMap<>();params.put("Constant","我是Native常量,传递给RN");return params;}
    }
    
  • 创建 MyReactPackage.java
    /**
    * 类名称:MyReactPackage
    * 创建者:Create by liujc
    * 描述:RN包管理器
    */
    public class MyReactPackage implements ReactPackage {public MyNativeModule myNativeModule;@Overridepublic List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {myNativeModule = new MyNativeModule(reactContext);List<NativeModule> modules = new ArrayList<>();//将我们创建NativeModule添加进原生模块列表中modules.add(myNativeModule);return modules;}@Overridepublic List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {//该处后期RN调用原生控件或自定义组件时可用到return Collections.emptyList(); }
    }
    
  • 将我们创建的MyReactPackage添加到我们在AppApplication中创建的ReactNativeHost中。
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() {return BuildConfig.DEBUG;}@Overrideprotected List<ReactPackage> getPackages() {return Arrays.<ReactPackage>asList(new MainReactPackage(),//将我们创建的包管理器给添加进来new MyReactPackage());}};
    
  • 接下来我们在js文件中如何调用?
    'use strict';import React, { Component } from 'react';
    import {AppRegistry,StyleSheet,Text,NativeModules,View,ToastAndroid,DeviceEventEmitter
    } from 'react-native';let title = 'React Native界面';class reactNative extends Component {/**加载完成*/componentWillMount() {let result = NativeModules.MyNativeModule.Constant;console.log('原生端返回的常量值为:' + result);}/*** 原生调用RN*/componentDidMount() {DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{title = "React Native界面,收到数据:" + msg;ToastAndroid.show("发送成功", ToastAndroid.SHORT);})}/*** RN调用Native且通过Callback回调 通信方式*/callbackComm(msg) {NativeModules.MyNativeModule.rnCallNativeFromCallback(msg,(result) => {ToastAndroid.show("CallBack收到消息:" + result, ToastAndroid.SHORT);})}/*** RN调用Native且通过Promise回调 通信方式*/promiseComm(msg) {NativeModules.MyNativeModule.rnCallNativeFromPromise(msg).then((result) =>{ToastAndroid.show("Promise收到消息:" + result, ToastAndroid.SHORT)}).catch((error) =>{console.log(error)});
    }/*** 调用原生代码*/call_button(){NativeModules.MyNativeModule.rnCallNative('调用原生方法操作');}callNative(){NativeModules.MyNativeModule.startActivityRN('com.liujc.rnappdemo.MyRNActivity','test');}render() {return ( <View style={styles.container}><Text style={styles.welcome} >{title}</Text><Text style={styles.welcome}onPress={this.call_button.bind(this)}>React Native 调用原生方法操作!</Text><Text style={styles.welcome}//给此处的文字绑定一个事件,其中callNative为要调用的方法名。onPress={this.callNative.bind(this)}>跳转MyRNActivity!</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></View>);}
    }const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF',},welcome: {fontSize: 20,textAlign: 'center',margin: 10,},instructions: {textAlign: 'center',color: '#333333',marginBottom: 5,},
    });AppRegistry.registerComponent('reactNative', () => reactNative);

三、 RN中加载Gif图片

需要添加facebook的两个图片加载库:(注意版本号尽量与你使用的RN版本内部使用的fresco版本保持一直)

implementation 'com.facebook.fresco:fresco:1.3.0'
implementation 'com.facebook.fresco:animated-gif:1.3.0'

作者:闲庭CC
链接:https://www.jianshu.com/p/79edec250158
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

RN系列:Android原生与RN如何交互通信相关推荐

  1. 客户端相关知识学习(三)之Android原生与H5交互的实现

    Android原生与H5交互的实现 H5调用原生的方式 方式可能有多种,根据开发经验,接触过两种方式. 方法一:Android向H5注入全局js对象,也就是H5调Android 1.首先对WebVie ...

  2. Android开发-Android原生与WebView的js交互的实现

    前 言 一般在Android应用的开发过程中,一些核心的业务功能开发往往需要Android原生的开发,一些需要改动比较频繁的功能且不是核心的业务功能往往只需要在Android应用调用一个网页就行了.在 ...

  3. android h5和原生方法调用,Android原生与H5通信

    如今,混合开发似乎成为了主流.H5拥有跨平台的优势,却存在性能上的问题正好可以用搭建原生壳承载H5代码的方式去解决. 如何理解这种方式,就好像说Android原生代码封装外壳,内部包含H5代码作为核心 ...

  4. android原生webview,Android 原生与WebView JS的交互

    webview调用java方法 1.允许webview加载js : webView.getSettings().setJavaScriptEnabled(true); 2.编写js接口类 import ...

  5. Android原生与h5js交互

    为了方便原生开发和Html之间数据传递,在静态页面的情况下可以改变原生开发的页面: 实现 新建一个Activity,布局里面放一个WebView 布局 <?xml version="1 ...

  6. React Native 与 嵌入Android原生与Activity页面互相跳转

    前言 RN作为混合开发,肯定需要与原生直接的页面跳转,这里也属于和原生端通信的知识模块.我们知道Android的页面跳转是通过Intent.Rn是通过路由,而两者直接页面互相跳转就需要原生借助JS暴露 ...

  7. Android原生h5互跳控制,Android原生与H5交互方式

    Demo 地址:码云 一.Android API提供的官方交互方式 1.webView.getSettings().setJavaScriptEnabled(true);设置webview运行执行js ...

  8. android原生与kotlin验证码倒计时

    一.Android原生倒计时代码 1.倒计时方法 /*** 获取验证码*/ public void getYanZhengMa(TextView btn_yanzhengma, EditText ed ...

  9. iOS开发-------基于WKWebView的原生与JavaScript数据交互

    WKWebView是iOS8.0之后用以替代UIWebView的网页浏览器,包含在WebKit中,可以通过 @import WebKit 导入.如果工程需要适配iOS7,那么请在iOS7中使用UIWe ...

  10. rn+与android+交互,React native 与Android原生交互方式(一)

    前言## 最近在做React Native开发的时候避免不了的需要原生模块和JS之间进行交互,其实RN和原生的通信大致分为两种情况:一种是Android主动向RN端发送事件和数据,另外一种是RN端被动 ...

最新文章

  1. linux关于tcp协议ack的实现--发送端对ack的处理
  2. 一次“失败”的阿里面试之旅
  3. 小程序音视频能力技术负责人解读“小程序直播”
  4. 富文本编辑器宽度自适应及取消自动保存功能
  5. 阿尔伯塔大学的计算机科学专业好吗,阿尔伯塔大学哪个专业好?三大热门方向成就高薪未来...
  6. Java学习笔记_继承
  7. akka linux 端口,Actor模型开发库 Akka
  8. python官网网址是什么意思_大家都是怎么部署python网站的?
  9. 厉害了,Spring中bean的12种定义方法!
  10. Intel 64/x86_64/IA-32/x86处理器 - SIMD指令集 - SSE扩展(4) - 数据传输指令
  11. FF:纳斯达克要求退市系误读 警示函仅与推迟提交Q3财报相关
  12. 数据架构总体设计方案
  13. jdk String类源码解析
  14. vs2015环境搭建与简单程序测试
  15. Redis入门整合springboot
  16. C语言 Linux版俄罗斯方块,C语言版俄罗斯方块源代码
  17. 工商企业大数据爬虫系统
  18. iconfont矢量图标在小程序中的使用
  19. vue2.0 axios封装
  20. ORA-00932: 数据类型不一致: 应为 CHAR, 但却获得 NUMBER

热门文章

  1. 求圆外一点做圆切线的切点坐标(算法)
  2. 一个IT从业者的课外读物___养生锻炼篇
  3. Google Bazel简介
  4. 学编程c语言高考能加分吗,编程已列入中高考,孩子升学加分的机会你抓住了么?...
  5. 在赛灵思ZCU102开发板上部署mnist手写体识别程序
  6. Flink之DataSet转换操作(二)
  7. 神奇的Perl-哈希与数组(4)
  8. 山西省大同市谷歌高清卫星地图下载
  9. 基频和倍频的概念_倍频技术
  10. Windows资源管理器已停止工作的解决方法