美文网首页
RN与原生互相通信

RN与原生互相通信

作者: lixianglei | 来源:发表于2021-03-25 16:39 被阅读0次

    最近负责一个项目。里面语言是用RN开发的,现在想把RN逐步替换为原生进行开发,整理一下关于RN与原生之间相互调用的方法。

    简述RN集成到Android原生项目:
    https://www.jianshu.com/p/f546ad231382

    页面跳转和方法调用

    一、页面跳转(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进行了封装映射,我们只需要把数据传送到对应的位置即可,

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

    通过上面源码也就了解了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 和 RN调用原生 为例简单描述下:

    1、原生调用RN

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

    RN中接收原生消息:

    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;

        }

        @Override

        public String getName() {

        //返回的这个名字是必须的,在rn代码中需要这个名字来调用该类的方法。

            return REACT_NATIVE_CLASSNAME;

        }

    //函数不能有返回值,因为被调用的原生代码是异步的,原生代码执行结束之后只能通过回调函数或者发送信息给rn那边。

        @ReactMethod

        public void rnCallNative(String msg){

            Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show();

        }

        @ReactMethod

        public 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

        */

        @ReactMethod

        public void rnCallNativeFromCallback(String msg, Callback callback) {

            String result = "hello RN!Native正在处理你的callback请求";

            // .回调RN,即将处理结果返回给RN

            callback.invoke(result);

        }

        /**

        * Promise

        * @param msg

        * @param promise

        */

        @ReactMethod

        public void rnCallNativeFromPromise(String msg, Promise promise) {

            Log.e(TAG,"rnCallNativeFromPromise");

            String result = "hello RN!Native正在处理你的promise请求" ;

            promise.resolve(result);

        }

        /**

        * 向RN传递常量

        */

        @Nullable

        @Override

        public 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;

        @Override

        public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

            myNativeModule = new MyNativeModule(reactContext);

            List<NativeModule> modules = new ArrayList<>();

            //将我们创建NativeModule添加进原生模块列表中

            modules.add(myNativeModule);

            return modules;

        }

        @Override

        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {

            //该处后期RN调用原生控件或自定义组件时可用到

            return Collections.emptyList();

        }

    }

    将我们创建的MyReactPackage添加到我们在AppApplication中创建的ReactNativeHost中。

    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 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'

    相关文章

      网友评论

          本文标题:RN与原生互相通信

          本文链接:https://www.haomeiwen.com/subject/lvcrhltx.html