美文网首页程序员Hybrid开发
React-Native与原生模块间的几种通信方式

React-Native与原生模块间的几种通信方式

作者: 秦砖 | 来源:发表于2017-07-23 00:13 被阅读329次

    每种语言都有自己的设计理念、语法、运行环境,这也导致了不同语言间相互交流通信时必须要有中介来翻译,如JAVA与C/C++通过JNI来交流、OC与C/C++需要在.mm文件混编、而JAVA/OC与Lua通信时需要通过C/C++语言来做中介。那么在React-Native中JSX是如何与底层模块进行通信的呢?这里主要以iOS系统来做说明。

    原理

    通信本质上是信息的交流,具体到计算机语言则是数据的流动。应用中数据在React-Native与原生模块间的流动与共享,完成了与用户的交互,达成了应用的目标。React-Native与OC间通信的数据只能是下面的几种类型(前为JS类型,后为OC类型):

    • string-NSString
    • number - int/NSInteger/float/double/NSNumber
    • boolean - BOOL/NSNumber
    • array - NSArray
    • object - NSDictionary(NSString型key, value可以为这里的其它类型)
    • func - RCTResponseSenderBlock

    其它类型的数据需要通过一定的规则转换成这几种类型后(一般都会转换成JSON串)再通信.

    React-Native本质是通过JavaScriptCore.framework实现JS代码与OC代码间的互动。因此下面说的几种方式在本质原理上都是相同的,不同的地方只是在于实现形式与方法的差别。

    函数调用

    在将原生模块封装并提供给React-Native使用时,可以通过RCT_EXPORT_METHOD()宏向React-Native侧定义其可以调用的接口函数,完成两模块间的通信。

    //定义了startVPN接口,React-Native将VPN的具体参数通过该接口传入到原生模块,开启指定的VPN
    RCT_EXPORT_METHOD(startVPN:(NSDictionary*)config)
    {
      LSShadowSocksDataMode* mode = [[LSShadowSocksDataMode alloc] initWithDictionary:config];
      [self.manager startVPN:mode];
    }
    

    除了传入数据外,通过可以通过这种方式从原生侧获取数据。最容易想到的是通过返回值获取,可惜的是RCT_EXPORT_METHOD宏不支持返回值,不过其提供了另外一种实现返回值的方式:

    RCT_EXPORT_METHOD(isOpen:(RCTResponseSenderBlock)callback)
    {
      BOOL open = [self.manager status];
      callback(@[[NSNull null], @[@(open)]]);
    }
    

    通过回调函数的形式实现返回值的效果,达到了数据交换的目的。

    属性共享

    这种方式主要针对于UI控件来说的。React-Native中最基础的UI类型是RCTRootView,该类有一个初始化方法initWithBridge:moduleName:initialProperties:,第三个参数initialProperties表示的是UI控件的初始属性值,类型为NSDictionary,其最终会被同步到由第二个参数定义的React-Native类的props中,即完成了两个模块间的数据交流。

    NSArray *imageList = @[@"http://foo.com/bar1.png",
                      @"http://foo.com/bar2.png"];
    
    NSDictionary *props = @{@"images" : imageList};
    
    RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                          moduleName:@"ImageBrowserApp"
                                         initialProperties:props];
    
    import React, { Component } from 'react';
    import {
      AppRegistry,
      View,
      Image,
    } from 'react-native';
    
    class ImageBrowserApp extends Component {
      renderImage(imgURI) {
        return (
          <Image source={{uri: imgURI}} />
        );
      }
      render() {
        return (
          <View>
            {this.props.images.map(this.renderImage)}
          </View>
        );
      }
    }
    
    AppRegistry.registerComponent('ImageBrowserApp', () => ImageBrowserApp);
    

    初始化接口只能在UI组件建立时使用,如果需要在UI组件的生命周期内通信呢,RCTRootView提供了appProperties这样一种机制:

    NSArray *imageList = @[@"http://foo.com/bar3.png",
                       @"http://foo.com/bar4.png"];
    rootView.appProperties = @{@"images" : imageList};
    

    通知

    OC中使用NSNotificationCenter向整个应用发送通知,所有对该通知感兴趣的对象都会获得该通知并执行相应的动作。React-Native中也提供有类似的机制:RCTEventEmitter。原生模块继承该类后,就可以向React-Native侧发送通知,而React-Native就能够接收到该通知,并处理一并传送过来的数据了。

    -(void)vpnStatusChanged:(NSNotification*)notification
    {
      NEVPNStatus status = [self.manager status];
      NSString* value = nil;
      switch (status) {
        case NEVPNStatusReasserting:
          value = @"重新连接中";
          break;
        case NEVPNStatusConnecting:
          value = @"连接中";
          break;
        case NEVPNStatusConnected:
          value = @"已连接";
          break;
        case NEVPNStatusDisconnecting:
          value = @"断开连接中";
          break;
        case NEVPNStatusDisconnected:
        case NEVPNStatusInvalid:
          value = @"末连接";
          break;
        default:
          break;
              }
      if(value){
        [self sendEventWithName:@"VpnStatus" body:@{@"status":value}];
      }
    }
    

    这里将VPN的状态通过通知发送到React-Native侧,由React-Native将VPN的状态显示的UI界面上。

    小结

    这里只是简单的介绍了两种语言间几种常用的通信方式,并没有涉及到其背后的实现细节。对这方面感兴趣的同学,不妨参阅下面的两篇文章:
    React Native通信机制详解
    浅析ReactNative之通信机制

    相关文章

      网友评论

        本文标题:React-Native与原生模块间的几种通信方式

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