RN与原生互相通信

最近负责一个项目。里面语言是用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 getConstants() {

        Map 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 createNativeModules(ReactApplicationContext reactContext) {

        myNativeModule = new MyNativeModule(reactContext);

        List modules = new ArrayList<>();

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

        modules.add(myNativeModule);

        return modules;

    }

    @Override

    public List 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 getPackages() {

        return Arrays.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 (

   

         

          {title}

         

         

          onPress={this.call_button.bind(this)}

          >

            React Native 调用原生方法操作!

         

       

                //给此处的文字绑定一个事件,其中callNative为要调用的方法名。

                  onPress={this.callNative.bind(this)}>

                  跳转MyRNActivity!

         

       

            Callback通信方式

       

       

            Promise通信方式

       

   

    );

  }

}

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与原生互相通信)