[React Native]原生模块(下)

作者: zhuhf | 来源:发表于2016-05-26 23:09 被阅读783次

    上一篇文章[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

    相关文章

      网友评论

      • 幸福的李雨龙:lz怎么自定义event?只是简单拿官网栗子有点没诚意啊
      • putaozhuose:在js中Promise不是必须和async一起使用的。不知道楼主是不是这个意思
        zhuhf:@putaozhuose 不好意思,之前没做过前端开发。 es7语法我测试过,是必须一起使用,具体原因我也不知道。
      • putaozhuose:源码是NativeModule,这个名字和import com.facebook.react.bridge.NativeModule; 冲突,也是可以的吗?还是本意用NativeAndroid ?
        zhuhf:@putaozhuose 也是可以的,不过这个类的名称不是重点,关注getName()返回的值就可以了。
      • 7b01b20d0280:callback跟promise是不是同个东西 就写法不同啊?
        zhuhf:@zhongjiechen 我的理解是一一致,功力比较浅,纯属个人看法

      本文标题:[React Native]原生模块(下)

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