RN

React-Native 与原生层之间的通信

2018-05-14  本文已影响84人  e8f86562b6f2

写在前面:在你编写react-native应用的时候,有些时候是需要原生层的api来完成特定功能的;举个栗子:调起拨打电话界面,蓝牙通信,wifi协议等等;所以RN与原生代码通讯对于混合编程是至关重要的。

为了实现JS与Native之间的通信,facebook也提供了三种通信方式。分别是:

1.  EventEmitter消息机制:由Native发起,可以任意时刻传递到JS;
2.  Callback回调方式:由JS调用,原生代码响应方返回;
3.  Promise机制方式:由JS调用,只是每次使用都需要调用;

1.JS与IOS原生通信:

RnModule.h 文件

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

// 继承自RCTEventEmitter
@interface RnModule : RCTEventEmitter<RCTBridgeModule>

@end

RnModule.m 文件

#import "RnModule.h"

@implementation RnModule

///此名供JS调用,一旦声明,该类的实例已经创建好了
RCT_EXPORT_MODULE(rn);

- (NSArray<NSString *> *)supportedEvents
{
  return @[@"ar"];
}

/** 注意:ios 更新ui只能再主线程更新   **/

///native 向 js 发送
RCT_EXPORT_METHOD(event:(NSString *)query){
///这里可以进行一系列原生操作
[self sendEventWithName:@"ar" body:@{@"message":@"native--》rn",@"data":query}];
};

///Callback方式,异步返回
RCT_EXPORT_METHOD(eventCallback:(RCTResponseSenderBlock)callback){
 ///这里可以进行一系列原生操作
 NSString *msg = @"我是==>>Callback回调";
  callback(@[msg]);
 };

///Promise,异步返回
RCT_REMAP_METHOD(eventPromise,
             resolver:(RCTPromiseResolveBlock)resolve
             rejecter:(RCTPromiseRejectBlock)reject){
  ///这里可以进行一系列原生操作
  NSString *msg = @"我是==>>Promise回调";
  resolve(msg);
};
@end

2.JS与Andoird原生通信:

CustomPackage.java文件

package com.demo.JSModule;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CustomPackage implements ReactPackage {

@Override
public List<NativeModule> createNativeModules(
        ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();
    modules.add(new RnModule(reactContext));
    return modules;
}

public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
}

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

RnModule.java文件

package com.demo.JSModule;

import com.facebook.react.bridge.Callback;
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.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class RnModule extends ReactContextBaseJavaModule {

public static final String RN_NAME = "rn";

private ReactApplicationContext mContext;

public RnModule(ReactApplicationContext reactContext) {
    super(reactContext);
    mContext = reactContext;
}

/**
 * 此名供JS调用,不设置将不曝露给js,会报错
 */
public String getName() {
    return RN_NAME;
}

/**
 * 用@ReactMethod 注解标注:表明该方法会被JS调用。
 * native 向 js 发送
 */
@ReactMethod
public void event(String query) {
     //做一系列原生操作
    executeResult("ar", "native--》rn", query);
}

/**
 * Callback方式,异步返回
 */
@ReactMethod
public void eventCallback(Callback Callback) {
     //做一系列原生操作
    String msg = "我是==>>Callback回调";
    Callback.invoke(msg);
}

/**
 * Promise,异步返回
 */
@ReactMethod
public void eventPromise(Promise Promise) {
    //做一系列原生操作
    String msg = "我是==>>Promise回调";
    Promise.resolve(msg);
}

/**
 * native 向 js 发送 函数
 */
private void executeResult(String methodName, String message, String data) {
    WritableMap writableMap = new WritableNativeMap();
    writableMap.putString("message", message);
    writableMap.putString("data", data);
    mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(methodName, writableMap);
}
}

然后再MainApplication.java中添加

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

3.js代码

/**
 *@ time: 2018/3/28
 *@ author: 二 白
 *@ use:
 */
 import React, {Component} from 'react';
 import {
      View,
      DeviceEventEmitter,
      NativeModules,
      Text,
      NativeEventEmitter
  } from 'react-native';
 import {Button, Toast} from 'teaset';

 export default class Test1 extends Component {

   constructor(props) {
      super(props);
     this.state = {
         value: '',
         message: ''
     };
  }

   componentWillMount() {
     if (iOS) {
        // 拿到原生模块
        const {rn} = NativeModules;

      // 创建自定义事件接口
       const iosManagerEmitter = new NativeEventEmitter(rn);

       this.iosListener = iosManagerEmitter.addListener("ar", (msg) => {
         this.setState({
           value: msg.data,
           message: msg.message
        });
  });
}
else {
  this.androidListener = DeviceEventEmitter.addListener('ar', (msg) => {
    this.setState({
      value: msg.data,
      message: msg.message
    });
  });
 }
}

componentWillUnmount() {
  //组件卸载的是否需要移除监听
  if (iOS) this.iosListener && this.iosListener.remove();
  else this.androidListener && this.androidListener.remove();
 }

 AN2RN = (e) => {
   if (e === 1) {
    NativeModules.rn.event('我来了!');
   }
   else if (e === 2) {
  NativeModules.rn.eventCallback((callback) => {
    Toast.info(callback);
  });
}
else {
  NativeModules.rn.eventPromise().then((promise) => {
    Toast.info(promise);
  });
 }
};

// 等待Promise对象返回后才执行,在某一些业务上很重要
async es() {
  const info = await NativeModules.rn.eventPromise();
  Toast.info(info);
};

render() {
const {value, message} = this.state;

return (
  <View style={{
    flex: 1,
    backgroundColor: '#FFFFFF'
  }}>
    <View style={{marginTop: px2dp(200), marginHorizontal: px2dp(100)}}>
      <Button onPress={() => {
        this.AN2RN(1)
      }} type='secondary' size='lg' title='native主导,任意时刻触发1'/>
    </View>

    <View style={{marginTop: px2dp(80), marginHorizontal: px2dp(100)}}>
      <Button onPress={() => {
        this.AN2RN(2)
      }} type='secondary' size='lg' title='callback方式fuck'/>
    </View>

    <View style={{marginTop: px2dp(80), marginHorizontal: px2dp(100)}}>
      <Button
        onPress={() => {
          this.AN2RN(3)
        }} type='secondary' size='lg' title='Promise方式'/>
    </View>

    <View style={{marginTop: px2dp(80), marginHorizontal: px2dp(100)}}>
      <Button
        onPress={this.es} type='secondary' size='lg' title='Promise(async/await)'/>
    </View>

    <View style={{marginTop: px2dp(20), marginHorizontal: px2dp(100)}}>
      {
        value ? <Text style={{fontSize: FONT_SIZE(20)}}>
            data:{value},
            message:{message}
          </Text>
          : null
      }
    </View>
  </View>
   );
 }
}

注意:
1.需要注意的地方都在代码中注释了;
2.js代码写法上在不同的平台上有一点小差异,需要区分出来;
3.这里只是用代码简单描述了一下,可以把本项目clone下来,看具体运行效果;
4.复杂的业务还是需要原生的知识,如果没有时间去学习原生知识的话,社区有封装好的相关组件,只需了解其API,js调用即可;

代码地址:https://github.com/huangnan1994/rn-demo

上一篇 下一篇

猜你喜欢

热点阅读