React Native iOS混合开发实战教程

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

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

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

几种通讯场景:git

  • 初始化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的方式,这种传递数据的方式比下文中所讲的其余几种传递数据的方式发生的时机都早。github

由于不多有资料介绍这种方式,因此可能有不少朋友还不知道这种方式,不过没关系,接下来我就向你们介绍如何使用这种方式来传递数据给JS。react-native

概念

RN容许咱们在初始化JS页面时向顶级的JS 组件传递props数据,顶级组件能够经过this.props来获取这些数据。bash

iOSless

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

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

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];
复制代码

coding.imooc.com/lesson/89.h… 另外,记得在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的链式调用方式。

参考

相关文章
相关标签/搜索