RN与原生通讯(安卓篇)

明天和意外你永远都不知道哪一个先来,编程界亦是如此。例如某个已经有原生代码开发模块的项目要求用RN扩张某些功能;又例如,RN中未封装到的组件非得求助于原生代码。所以RN与原生代码通讯对于混合编程是至关重要的。为了实现两者之间的通信,facebook也提供了三种通信方式。

看内容.jpg
  • RCTDeviceEventEmitter消息机制:由Native主导控制,可以任意时刻传递
  • Callback回调方式:由js代码调用,原生代码返回。
  • Promise机制方式:由js调用,只是每次使用都需要调用。

一、RN调用安卓代码(简单)

RN调用安卓原生的代码,大致分为如下几步。
1、用Android Studio打开一个已经创建好的RN项目,选择android/build.gradle文件。

01.jpg

2、创建一个类继承ReactContextBaseJavaModule,在该类中我们应该要暴露出一些让RN调用的方法,封装成一个原生模块。

  • 新建一个类MyReactPackage继承ReactContextBaseJavaModule
02.jpg
public class MyNativeModule extends ReactContextBaseJavaModule{
}
  • 实现getName方法。该方法用于返回RN代码需要寻找的类的名称。(alt+enter快捷键可快速实现父类方法)
 @Override
    public String getName() {
        // 一定要有名字 RN代码要通过名字来调用该类的方法
        return "ToastModule";
    }
  • 实现类的构造方法。在这里将传入的上下文赋值给类内部私有的上下文
 // 创建一个上下文,放到构造函数中,得到reactContext
    private ReactApplicationContext mContext;

    public MyNativeModule(ReactApplicationContext reactContext){

        super(reactContext);

        mContext = reactContext;
    }
  • 创建暴露给RN调用的方法。但是要用注释符号@ReactMethod修饰。
 //方法不能返回值 因为被调用的原生代码是异步的 原生代码执行结束之后只能通过回调函数或者发送消息给RN
    @ReactMethod
    public void rnCallNative(String msg){
        //这个方法是说弹出一个弹窗到界面Toast.makeText(mContext,msg,Toast.LENGTH_LONG).show();
    }

3、在原生代码中创建一个类实现接口ReactPackage包管理器,并且把第二步已经创建好的类加入到原生模块列表里。

实现接口ReactPackage的方法,当然我们现在只需要创建模块,所以在其他的实现先直接返回空集合即可。而在createNativeModules方法中,要先声明一个装有原生模块的列表。之后将原生模块加入到列表里。

public class MyReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new MyNativeModule(reactContext));
        return modules;
    }


    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

}

4、将创建好的包管理器添加到ReactPackage列表里,也就是MainApplication代码中

在类里找到方法getPackages方法,将包管理器添加进去。

 @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
              new MyReactPackage()
      );
    }

5.在RN代码中用NativeModules组件去调用原生模块

  • 导入组件
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    NativeModules,
} from 'react-native';
  • 设置方法调用原生代码
 call_button(){
        NativeModules.ToastModule.rnCallNative('RN与安卓开发');
    }
  • 布置UI
    在render方法里面设置当用户点击文字时,调用自定义的方法call_button。并且以这种形式创建的方法需要进行绑定
render() {
        return(
            <View style={styles.container}>
                <Text  onPress={this.call_button.bind(this)}>测试原生通讯</Text>
            </View>
        );
    }
  • 处理好样式
const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:'deeppink',
        flexDirection:'row',
        justifyContent:'center',
        alignItems:'center'    },
});

至此,基本的RN调用安卓原生代码的方式就得以实现。再从RN的角度来回看整个过程。RN调用原生的方法,此时安卓的application就会启动,完成之后它会去找Package的列表,进而找到自己创建的列表。而在组件的列表里面有一个原生模块列表,到自己的模块列表里面调用模块里的方法就完成了调用。

效果图如下:

03.jpg

二、RN用消息机制方式与安卓原生代码切换

实现效果:在原生代码中添加一个按钮,当用户从RN界面调用原生代码就会进入到原生代码开发的界面中,而点击原生代码中的按钮就会返回到RN界面。


树自行车分割线

接上一节的代码。
1、在与MainApplication同级的目录下创建一个Activity。Activity是android系统最小的调度单位。

04.jpg

创建名称为MyActivity的空活动。它会帮助我们生成一个自动布局文件做布局的工作。(此时若遇到错误,可以选择build->clean)

05.jpg

2、command+enter点击进入activity_my中,此时会打开布局文件。将左下角的Design切换成Text文件。在该文件中,为原生界面创建一个按钮并且布局。


06.jpg

xmlns:android表示设置xmlns的命名空间,没有这句话就无法设置属性的约束。
在该界面上创建一个按钮,为按钮绑定一个方法onBack。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:layout_width = "match_parent"
    android:layout_height="match_parent"
    xmlns:android = "http://schemas.android.com/apk/res/android"
    >

    <Button
        android:text="goBack"
        android:onClick="onBack"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</RelativeLayout>

3、回到新创建的MyActivity代码,实现onBack方法。

  //点击按钮,直接完成
    public void onBack(View v){
        finish();
    }

4、到MyNativeModule原生模块中去实现Activity

上文中已经说过,Activity是android系统的最小调度单位,而Intent则是安卓的进程之间、activity之间、线程之间交换数据的载体。

 //方法不能返回值 因为被调用的原生代码是异步的 原生代码执行结束之后只能通过回调函数或者发送消息给RN
    @ReactMethod
    public void rnCallNative(String msg){

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

        Intent intent = new Intent(mContext,MyActivity.class);  //创建一个意图,意图是android进程之间、线程之间、交换数据的载体
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    //一定要加上这句
        mContext.startActivity(intent);
    }

效果图如下:

07RN与原生切换.gif

三、RN用Promise机制与安卓原生代码通信

使用Promise机制也是RN与原生通信的一种方式。在原生代码的MyNativeModule文件中创建桥接方法。当桥接的原生方法的最后一个参数是一个Promise对象,那么该方法会返回一个JS的Promise对象给与之对应的js方法。

与上文类似,需要暴露给RN的方法不能有返回值,并且要以注释@ReactMethod标识。

  @ReactMethod
    public void rnCallNative_promise(String msg,Promise promise){
        Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show();
        //得到组件名称
        String componentName = getCurrentActivity().getComponentName().toString();
        promise.resolve(componentName);
    }

原生代码已经实现。接下来要实现的就是RN的代码。在RN中创建一个方法,这个方法内部使用NativeModules组件来调用原生模块提供的名称,进而找到要调用的原生方法。原生方法最后一个参数是一个promise,所以在js中用.then的方法实现即可。

 callAndroid_promise(){
        NativeModules.ToastModule.rnCallNative_promise('promise调用原生').then(
                (msg) => {
                    console.log('promise收到消息:'+msg);
                }
            ).catch(
                (err)=>{
                    console.log(err);
                }
            )
    }

到渲染方法中,调用方法

 <Text style={styles.welcome} onPress={this.callAndroid_promise.bind(this)}>Promise通信</Text>

给Text组件设置样式

 welcome: {
        fontSize: 16,
        textAlign: 'left',
        margin: 10
    }

结果图如下:

08.jpg

用Debug进行调试,得到结果如下:

09.jpg

四、RN用callback回调方式与安卓原生代码通信

按照上文中提到的方式,在原生模块中暴露一个桥接方法给RN调用。
参数传入一个成功的回调和一个失败的回调。

@ReactMethod
    public void measureLayout(Callback errorCallback,Callback successCallback){
        try {
            successCallback.invoke(100,100,200,200); //调用回调函数,返回结果
        }catch (IllegalViewOperationException e){
            errorCallback.invoke(e.getMessage());
        }
    }

在js中实现回调方法。同样是通过NativeModules组件寻找到桥接名称ToastModule,进而找到想要调用的方法。拿到返回的参数,做功能处理。

 callAndroid_callback(){
        NativeModules.ToastModule.measureLayout(
            (msg)=>{
                console.log(msg);
            },
            (x,y,width,height)=>{
                console.log('x坐标:'+x+'y坐标:'+y+'高:'+height+'宽'+width);
            }
        )
    }
10.jpg

在Debug调试下,点击callback通信文字,可以看到如下结果:

11.jpg

在使用回调函数时会呈现出某些缺点,比如说每次调用只应当调用一次,多次调用可能会出现意想不到的结果,并且用这种方法安卓原生代码是无法主动发送信息给RN侧的。而消息机制的方式就可以进行消息的互相传递。


小孩与狗童真分隔线
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,158评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,600评论 1 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,785评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,655评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,075评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,002评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,146评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,918评论 0 211
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,671评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,838评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,318评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,636评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,343评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,187评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,982评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,126评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,934评论 2 279

推荐阅读更多精彩内容