[React Native]原生模块(下)

上一篇文章[React Native]原生模块(上)中我们介绍了原生模块的基本用法,本篇文章将介绍原生模块的高级用法—ReactNative与原生模块的通信方式。

由于本文涉及到很多React方面的知识,比如state、生命周期等,没有接触过的同学建议首先阅读下阮一峰老师的React入门实例教程

通信方式总共分为以下三种:

  • Callback
  • Promise
  • Event

本文将演示原生模块使用Thread异步执行方法后回调JS模块,并在界面上显示出线程的名字和执行时间,效果图如下

device-2016-05-26-214323_0-87.gif

首先,按照上一篇文章[React Native]原生模块(上)的步骤
1、新建一个ReactNative项目
2、并创建名称为NativeAndroid的原生模块
3、最后完成他的注册工作
此时,NativeAndroid的代码如下

public class NativeModule extends ReactContextBaseJavaModule {
  ReactApplicationContext mReactContext;
  public NativeModule(ReactApplicationContext reactContext) {
      super(reactContext);
      mReactContext = reactContext;
  }

  @Override
  public String getName() {
      return "NativeAndroid";
  } 
}

4、在index.android.js中用TouchableOpacity包裹三个可点击的ViewCallbackPromiseEvent

render() {
  return (
    <View style={styles.container}>
      <TouchableOpacity onPress={()=>{
        
        }}>
        <Text style={styles.welcome}>
          Callback
        </Text>
      </TouchableOpacity>
    
      <TouchableOpacity onPress={()=>{
        
        }}>
        <Text style={styles.welcome}>
          Promise
        </Text>
      </TouchableOpacity>
    
      <TouchableOpacity onPress={()=>{
        
        }}>
        <Text style={styles.welcome}>
          Event
        </Text>
      </TouchableOpacity>
    </View>
  );
}

5、在index.android.js中新建一个stateresult

简单理解就是一个特殊的属性,一旦修改它的值,会触发render方法,这样页面就得到刷新了。对这个用法不了解的同学,可以参考阮一峰老师的React入门实例教程

// es6语法,如果使用React.createClass则使用getInitialState
constructor () {
  super();
  this.state = {
    result: ''
  }
}

render() {
  return (
    <View style={styles.container}>
      // 此处代码省略若干行
      <Text style={styles.instructions}>
        {this.state.result}
      </Text>
    </View>
  );
}

准备步骤到此结束了。

下面,我们分别介绍三种实现方式

  • Callback

回调函数,对应JS中的function

1、在NativeModule中新建testCallback方法 ,包含一个参数Callback(对应JSfunction

  @ReactMethod
  public void testCallback(final Callback callback)
  {
      new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(3000);
                  callback.invoke("Callback: " + Thread.currentThread().getName() + " has slept 3 ms.");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }).start();
  }

2、在CallbackonPresss方法中调用testCallback方法

  <TouchableOpacity onPress={()=>{
    NativeAndroid.testCallback((msg)=>{
          this.setState({result: msg});
        });
    }}>
    <Text style={styles.welcome}>
        Callback
    </Text>
  </TouchableOpacity>

这里给testCallback方法传递了一个箭头函数,并将返回值msg赋值给result,然后会触发render方法刷新界面。

  • Promise

这里涉及到ES7标准的JS语法,需要配合async/await来使用。桥接的原生方法的最后一个参数为Promise

1、在NativeModule中新建testPromise方法 ,包含一个参数Promise(当然,你也可以同时传递其他参数)

  @ReactMethod
  public void testPromise(final Promise promise)
  {
      new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(3000);
                  WritableMap map = Arguments.createMap();
                  map.putString("msg", "Promise: " + Thread.currentThread().getName() + " has slept 3 ms.");
                  promise.resolve(map);
              } catch (InterruptedException e) {
                  promise.reject("InterruptedException", e);
              }
          }
      }).start();
  }

这里,使用promise.resolve来传递一个WritableMap,使用promise.reject来传递错误信息

WritableMap类似一个字典,用来包装多个输出参数给JS模块

2、在index.android.js文件的class新建一个方法promise,方法需要使用async来修饰

  // es7语法,Promise必须配合async使用
  async promise()
  {
    try {
      var {
        msg
      } = await NativeAndroid.testPromise();
      this.setState({result: msg});
    } catch (error) {
      console.log(error);
    }
  }

这里,var {msg} = await NativeAndroid.testPromise();中的msg对应原生方法public void testPromise(final Promise promise)的语句promise.resolve(map);map传递的参数msg,最后使用this.setState({result: msg});来刷新界面

3、在PromiseonPress方法中调用promise方法

  <TouchableOpacity onPress={()=>{
      this.promise();
    }}>
    <Text style={styles.welcome}>
      Promise
    </Text>
  </TouchableOpacity>
  • Event

1、原生模块可以在没有被调用的情况下往JavaScript发送事件通知。最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获得对应的引用,像这样:
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("事件名称", Object);

2、原生模块可以主动通知JS模块,这里为了简单起见,还是通过JS触发原生方法,原生方法再发送事件到JS模块

1、在NativeModule中新建testEvent方法

  /**
   * 发送事件到JS
   * [为了简单演示,从JS调用此方法再发送事件到JS]
   */
  @ReactMethod
  public void testEvent()
  {
      new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(3000);
                  mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                        .emit("testEvent", "Event: " + Thread.currentThread().getName() + " has slept 3 ms.");
              } catch (InterruptedException e) {
              }
          }
      }).start();
  }

2、在JS模块组件Component的生命周期函数componentWillMount中注册事件监听,并在收到通知后刷新界面

  componentWillMount()
  {
     DeviceEventEmitter.addListener('testEvent', (msg) => {
       this.setState({result: msg});
     });
  }

当然,你需要引入DeviceEventEmitter

  import {
    // ...省略若干行代码
    DeviceEventEmitter
  } from 'react-native';

3、在EventonPress方法中调用testEvent方法

  <TouchableOpacity onPress={()=>{
        NativeAndroid.testEvent();
      }}>
      <Text style={styles.welcome}>
        Event
      </Text>
  </TouchableOpacity>

最后,让我们再次看下最终效果图

device-2016-05-26-214323_0-87.gif

本文的源码地址Demo3

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

推荐阅读更多精彩内容