React Native iOS混合开发

在做RN开发的时候通常离不了JS 和Native之间的通信,比如:初始化RN时Native向JS传递数据,JS调用Native的相册选择图片,JS调用Native的模块进行一些复杂的计算,Native将一些数据(GPS信息,陀螺仪,传感器等)主动传递给JS等。

在这篇文章中我将向大家介绍在RN中JS和Native之间通信的几种方式以及其原理和使用技巧;

接下来我将分场景来介绍JS 和Native之间的通信。

几种通信场景:

  • 初始化RN时Native向JS传递数据;
  • Native发送数据给JS;
  • JS发送数据给Native;
  • JS发送数据给Native,然后Native回传数据给JS;
React-Native-JS-Native-Communication

1. 初始化RN时Native向JS传递数据

init-data-to-js

在RN的API中提供了Native在初始化JS页面时传递数据给JS的方式,这种传递数据的方式比下文中所讲的其他几种传递数据的方式发生的时机都早。

因为很少有资料介绍这种方式,所以可能有很多朋友还不知道这种方式,不过不要紧,接下来我就向大家介绍如何使用这种方式来传递数据给JS。

概念

RN允许我们在初始化JS页面时向顶级的JS 组件传递props数据,顶级组件可以通过this.props来获取这些数据。

iOS

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                moduleName: self.moduleName //这个"App1"名字一定要和我们在index.js中注册的名字保持一致
                         initialProperties:@{@"params":self.paramsInit}//RN初始化时传递给JS的初始化数据
                             launchOptions: nil];

接下来,我们先来看一下如何在iOS上来传递这些初始化数据。

iOS向RN传递初始化数据initialProperties

RN的RCTRootView提供了initWithBundleURL方法来渲染一个JS 组件,在这个方法中提供了一个用于传递给这个JS 组件的初始化数据的参数。

方法原型:

- (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *)moduleName
        initialProperties:(NSDictionary *)initialProperties launchOptions:(NSDictionary *)launchOptions
  • jsCodeLocation:要渲染的RN的JS页面的路径;
  • moduleName:要加载的JS模块名;
  • initialProperties:要传递给顶级JS组件的初始化数据;
  • launchOptions:主要在AppDelegate加载JS Bundle时使用,这里传nil就行;

通过上述方法的第三个参数就可以将一个NSDictionary类型的数据传递给顶级JS组件

示例代码:

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                moduleName: self.moduleName
                         initialProperties:@{@"params":@"这是传递给顶级JS组件的数据"}//RN初始化时传递给JS的初始化数据
                             launchOptions: nil];

在上述代码中,我们将一个名为params的数据这是传递给顶级JS组件的数据传递给了顶级的JS 组件,然后在顶级的JS 组件中就可以通过如下方法来获取这个数据了:

 render() {
        const {params}=this.props;
        return (
            <View style={styles.container}>
                <Text style={styles.data}>来自Native初始化数据:{params}</Text>
            </View>
            );
 }

另外,如果要在非顶级页面如CommonPage中使用这个初始化数据,则可以通过如下方式将数据传递到CommonPage页面:

export default class App extends Component<Props> {
    ...
    render() {
        return <CommonPage  {...this.props}/>;
    }
    ...
}

2. Native到JS的通信(Native发送数据给JS)

init-data-to-js

在RN的iOS SDK中提供了一个RCTEventEmitter接口,我们可以通过该接口实现Native到JS的通信,也就是Native将数据传递给JS。

方法原型:

- (void)sendEventWithName:(NSString *)name body:(id)body;

所以只要我们获得RCTEventEmitter的实例就可以借助它将数据传递给JS。为了获得RCTEventEmitter的实例我们可以通过继承RCTEventEmitter <RCTBridgeModule>的方式来实现:

DataToJSPresenter.h

/**
 * React Native JS Native通信
 * Author: CrazyCodeBoy
 * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface DataToJSPresenter : RCTEventEmitter <RCTBridgeModule>

@end

DataToJSPresenter.m

/**
 * React Native JS Native通信
 * Author: CrazyCodeBoy
 * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import "DataToJSPresenter.h"

@implementation DataToJSPresenter

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
    return @[@"testData"];
}
- (instancetype)init {
    if (self = [super init]) {//在module初始化的时候注册fireData广播
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fireData:) name:@"fireData" object:nil];
    }
    return self;
}
- (void)fireData:(NSNotification *)notification{//发送数据给RN
    NSString *eventName = notification.object[@"name"];
    NSDictionary *params = notification.object[@"params"];
    [self sendEventWithName:eventName body:params];
}

@end

在上述方法中,我们通过RCTEventEmittersendEventWithName方法将名为eventName的数据params传递给了JS。

提示:在DataToJSPresenter中我们实现了(NSArray<NSString *> *)supportedEvents方法,该方法用于指定能够发送给JS的事件名,所以发送给JS的eventName一定要在这个方法中进行配置否则无法发送。

实现Native到JS的通信所需要的步骤

接下来我们来总结一下,要实现Native到JS的通信所需要的步骤:

  • 首先要实现RCTEventEmitter <RCTBridgeModule>
  • 通过RCTEventEmittersendEventWithName方法将数据传递给JS;

通过上述步骤,我们就可以将数据从Native发动到JS,那么如何在JS中来获取这些数据呢?

在JS中获取Native通过RCTEventEmitter传过来的数据

在JS中可以通过NativeEventEmitter来获取Native通过RCTEventEmitter传过来的数据,具体方法如下:

import {NativeEventEmitter} from 'react-native';
export default class CommonPage extends Component<Props> {
    constructor(props) {
        super(props);
        this.state = {
            data: "",
            result: null
        }
    }

    componentWillMount() {
        this.dataToJSPresenter = new NativeEventEmitter(NativeModules.DataToJSPresenter);
        this.dataToJSPresenter.addListener('testData', (e) => {// for iOS
            this.setState({
                data: e.data
            })
        })
    }

    componentWillUnmount() {
        if (this.dataToJSPresenter){
            this.dataToJSPresenter.removeListener('testData');
        }
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.data}>收到Native的数据:{this.state.data}</Text>
            </View>
        );
    }
}

在上述代码中,我们通过NativeEventEmitteraddListener添加了一个监听器,该监听器会监听Native发过来的名为testData的数据,这个testData要和上文中所讲的eventName要保持一致:

[self sendEventWithName:eventName body:params];

https://coding.imooc.com/lesson/89.html#mid=2702
另外,记得在JS组件卸载的时候及时移除监听器。

以上就是在iOS中实现Native到JS通信的原理及方式,接下来我们来看一下实现JS到Native之间通信的原理及方式。

3. JS到Native的通信(JS发送数据给Native)

init-data-to-js

我们所封装的NativeModule就是给JS用的,它是一个JS到Native通信的桥梁,JS可以通过它来实现向Native的通信(传递数据,打开Native页面等),接下来我就来借助NativeModule来实现JS到Native的通信。

关于如何实现NativeModule大家可以学习参考React Native原生模的封装

首先实现JSBridgeModule

首先我们需要实现RCTBridgeModule

JSBridgeModule.h

/**
 * React Native JS Native通信
 * Author: CrazyCodeBoy
 * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import <React/RCTBridgeModule.h>
@interface JSBridgeModule : NSObject <RCTBridgeModule>

@end

JSBridgeModule.m

/**
 * React Native JS Native通信
 * Author: CrazyCodeBoy
 * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import "JSBridgeModule.h"

@implementation JSBridgeModule

RCT_EXPORT_MODULE();
- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();//让RN在主线程回调这些方法
}
RCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params){//接受RN发过来的消息
    [[NSNotificationCenter defaultCenter] postNotificationName:@"sendMessage" object:params];
}
@end

代码解析

  1. JSBridgeModule中,我们实现了一个RCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params)方法,该方法主要用于暴露给JS调用,来传递数据params给Native;
  2. 当收到数据后,通过NSNotificationCenter以通知的形式将数据发送出去;

JS调用JSBridgeModule发送数据给Native

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;

JSBridge.sendMessage({text: this.text})

通过上述代码我就可以将一个Map类型的数据{text: this.text}传递给Native。

4. JS发送数据给Native,然后Native回传数据给JS

init-data-to-js

通过上文所讲的JS到Native的通信(JS发送数据给Native),我们已经实现了JS到Native的通信,当时我们借助的是JSBridgeModule,其实它的功能还不局限于此,借助它我们还可以实现Native到JS的数据回传。

在Native的实现

JSBridgeModule中添加如下方法:

RCT_EXPORT_METHOD(doAdd:(NSInteger )num1 num2:(NSInteger )num2 resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    NSInteger result=num1+num2;
    resolve([NSString stringWithFormat:@"%ld",(long)result]);//回调JS
}

上述代码暴露给了JS一个简单的两个整数之间的加法运算,并将运算结果回传给JS,在这里我们用的是RCTPromiseResolveBlockRCTPromiseRejectBlock两种类型的回调,分别代表成功和失败。

在JS中的实现

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;
JSBridge.doAdd(parseInt(this.num1), parseInt(this.num2)).then(e => {
    this.setState({
        result: e
    })
})

在JS中我们通过JSBridge.doAdd方法将两个整数num1num2传递给了Native,然后通过then来监听回传结果,整个过程采用了Promise的链式调用方式。

参考

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

推荐阅读更多精彩内容