一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要。那么Himi先讲解React Native与iOS之间的通信交互。
本篇主要分为两部分讲解:(关于其中讲解的OC语法等不介绍,不懂的请自行学习)
1. React Native 访问iOS
2. iOS访问React Native
所以首先我们先创建一个oc新类, Himi这里起名为:TestOJO (O: object-c, J: javaScript )
#import <Foundation/Foundation.h> #import "RCTBridgeModule.h" @interface TestOJO : NSObject <RCTBridgeModule> @end
引入:#import “RCTBridgeModule.h” 且使用 <RCTBridgeModule> 接口,
4. 在TestOJO.m中添加如下:
RCT_EXPORT_MODULE(); //桥接到Javascript的方法返回值类型必须是void。React Native的桥接操作是异步的,所以要返回结果给Javascript,必须通过回调或者触发事件来进行 RCT_EXPORT_METHOD(j2oFun1:(NSString *)dataStringdateNumber:(int)dateNumber) { NSLog(@"js call iOS function j2oFun1/n dataString: %@ |dateNumber :%d",dataString,dateNumber); }
想要将oc的函数导出给js进行调用,那么就需要进行声明。声明通过RCT_EXPORT_METHOD()宏来实现:
j2oFun1:函数名,后续是两个参数,分别是NSString 和 int 类型数据。
调用成功后,我们输出这两个传来的值到控制台。
5. 下面看js调用的代码段:
var TestOJO = require('react-native').NativeModules.TestOJO; TestOJO.j2oFun1('Himi', 12321);
var TestOJO=require(‘react-native’).NativeModules.TestOJO;(将OC注册进来的模块取出)
TestOJO.j2oFun1(‘Himi’, 12321);(调用模块中的对应函数,且将参数进行传入)
OC 代码段(导出函数):
#import "RCTConvert.h" RCT_EXPORT_METHOD(j2oFun2:(NSDictionary *)details) { NSString *name = [RCTConvertNSString:details[@"name"]]; NSNumber *age = [RCTConvertNSNumber:details[@"age"]]; NSArray * array =[RCTConvertNSArray:details[@"array"]]; NSLog(@"js call iOS function j2oFun2/n name: %@ | age :%@", name, [agestringValue]); for (int i = 0; i<[arraycount]; i++) { NSLog(@"array: 第%d个元素:%@",i,array[i]); } }
需要注意的是,引入了”RCTConvert”类,作用:
JS代码段:(调用OC函数)
TestOJO.j2oFun2({ name:'Himi', age:12, array:[ 'hi,Himi','i,m','a array!' ] });
RCT_EXPORT_METHOD(j2oCallbackEvent:(NSString *)jsStringcallback:(RCTResponseSenderBlock)callback) { NSLog(@"js call iOS function: j2oCallbackEvent /n jsString:%@",jsString); NSArray *events = [[NSArray alloc]initWithObjects:@"Himi",@"12321", nil]; callback(@[[NSNull null], events]); }
RCTResponseSenderBlock 是种特殊的参数类型——回调函数,通过此参数可以实现当JS访问的OC函数后,并能将此OC函数的返回值传递给JS。
RCTResponseSenderBlock 只接受一个参数(传递给JavaScript回调函数的参数数组)
callback函数:第一个参数是一个错误对象(没有发生错误的时候为null),而剩下的部分是函数的返回值。
下面我们来看JS调用代码段:
TestOJO.j2oCallbackEvent('Himi',(error,callBackEvents)=>{ if (error) { console.error(error); } else { Alert.alert('J2O带返回值', '数组的三个值:/n[0]:'+callBackEvents[0]+'/n[1]:'+callBackEvents[1]+'/n[2]:'+callBackEvents[2]); } });
1. 我们如果想要OC访问JS,给JavaScript发送事件通知,我们需要使用RCTEventDispatcher的函数,与RCTBridge的实例
因此我们需要先做准备,TestOJO.h:
#import "RCTEventDispatcher.h" @synthesize bridge = _bridge;
bridge: 是RCTBridge 的实例,且在我们使用的接口 RCTBridgeModule中。
OC访问JS的代码段:
[self.bridge.eventDispatchersendAppEventWithName:@"eventName"body:@{@"name":@"Himi",@"age": @12}];
第一个参数:事件名
第二个参数(body):传入的参数
其中@{}是定义不可变的字典的快捷实例方式,因此我们也可以改成如下形式:
NSDictionary * direct =@{@"name": @"Himi",@"age": @12}; [self.bridge.eventDispatchersendAppEventWithName:@"eventName"body:direct];
下面来看JS中定义OC调用的函数:
其实所谓OC能响应JS,是JS进行了对应函数的绑定监听。因此我们需要利用 NativeAppEventEmitter 组件,利用其addListener进行注册监听!因此我们需要引入进来这个模块,
import { ... NativeAppEventEmitter ... } from 'react-native'; var o2cFun = NativeAppEventEmitter.addListener( 'eventName', (para) => Alert.alert('被OC触发','字典数据:/n name:'+para.name+'/n age:'+para.age) );
var o2cFun : 将绑定好的监听事件引用交给此变量保存。
addListener:
第一个参数:事件名
第二个参数:响应函数
注意:利用addListener进行监听,一定要对应有取消监听!要保持一一对应的好习惯。
且通常取消监听都在componentWillUnmount函数中进行。如下:
componentWillUnmount(){ o2cFun.remove(); }
其中对于原理并没有详细的介绍,这里推荐两篇文章,童鞋们可以详细的阅读一下,这里不赘述:
http://www.jianshu.com/p/203b91a77174
http://reactnative.cn/docs/0.21/native-modules-ios.html#content
TestOJO.h:
// // TestOJO.h // MyProject // // Created by Himi on 16/6/2. // Copyright © 2016年 Facebook. All rights reserved. // #import <Foundation/Foundation.h> #import "RCTBridgeModule.h" @interface TestOJO : NSObject <RCTBridgeModule> @end
TestOJO.m:
// // TestOJO.m // MyProject // // Created by Himi on 16/6/2. // Copyright © 2016年 Facebook. All rights reserved. // #import "TestOJO.h" //RCTConvert类支持的的类型也都可以使用,RCTConvert还提供了一系列辅助函数,用来接收一个JSON值并转换到原生Objective-C类型或类。 #import "RCTConvert.h" //本地模块也可以给JavaScript发送事件通知。最直接的方式是使用eventDispatcher #import "RCTEventDispatcher.h" @implementation TestOJO //====================================[JS -> OC]======================================= RCT_EXPORT_MODULE(); //桥接到Javascript的方法返回值类型必须是void。React Native的桥接操作是异步的,所以要返回结果给Javascript,必须通过回调或者触发事件来进行 RCT_EXPORT_METHOD(j2oFun1:(NSString *)dataStringdateNumber:(int)dateNumber) { NSLog(@"js call iOS function j2oFun1/n dataString: %@ |dateNumber :%d",dataString,dateNumber); } RCT_EXPORT_METHOD(j2oFun2:(NSDictionary *)details) { NSString *name = [RCTConvertNSString:details[@"name"]]; NSNumber *age = [RCTConvertNSNumber:details[@"age"]]; NSArray * array =[RCTConvertNSArray:details[@"array"]]; NSLog(@"js call iOS function j2oFun2/n name: %@ | age :%@", name, [agestringValue]); for (int i = 0; i<[arraycount]; i++) { NSLog(@"array: 第%d个元素:%@",i,array[i]); } } //带回调函数 RCTResponseSenderBlock ,提供将返回值传回给js //RCTResponseSenderBlock 只接受一个参数->传递给JavaScript回调函数的参数数组 RCT_EXPORT_METHOD(j2oCallbackEvent:(NSString *)jsStringcallback:(RCTResponseSenderBlock)callback) { NSLog(@"js call iOS function: j2oCallbackEvent /n jsString:%@",jsString); NSArray *events = [[NSArray alloc]initWithObjects:@"Himi",@"12321", nil]; callback(@[[NSNull null], events]); } //====================================[OC -> JS]======================================= @synthesize bridge = _bridge; //此函数是为了测试OC->JS过程,触发事件的函数 RCT_EXPORT_METHOD(emitterO2J) { [self ocCallJsFun]; } - (void)ocCallJsFun { NSDictionary * direct =@{@"name": @"Himi",@"age": @12}; [self.bridge.eventDispatchersendAppEventWithName:@"eventName"body:direct]; // [self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:@{@"name":@"Himi",@"age": @12}]; } @end
Main.js:
importReact, { Component } from 'react'; import { View, Text, StyleSheet, Image, Alert, NativeAppEventEmitter,//引用NativeAppEventEmitter组件进行监听Native端派发的事件 } from 'react-native'; var TestOJO = require('react-native').NativeModules.TestOJO; var o2cFun = NativeAppEventEmitter.addListener( 'eventName', (para) => Alert.alert('被OC触发','字典数据:/n name:'+para.name+'/n age:'+para.age) ); // 千万不要忘记忘记取消订阅, 通常在componentWillUnmount函数中实现。 // o2cFun.remove(); export default class Main extends Component { constructor(props) { super(props); this.state = { selectedTab:'home' }; } componentWillUnmount(){ o2cFun.remove(); } render() { return ( <Viewstyle={{flex: 1, alignItems: 'center'}}> <Text style={styles.himiTextStyle}>HimiReactNative 系列教程</Text> <Text onPress={()=>{ TestOJO.j2oFun1('Himi', 12321); TestOJO.j2oFun2({ name:'Himi', age:12, array:[ 'hi,Himi','i,m','a array!' ] }); TestOJO.j2oCallbackEvent('Himi',(error,callBackEvents)=>{ if (error) { console.error(error); } else { Alert.alert('J2O带返回值', '数组的三个值:/n[0]:'+callBackEvents[0]+'/n[1]:'+callBackEvents[1]+'/n[2]:'+callBackEvents[2]); } }); }} style={styles.himiTextStyle}>JS -> OC </Text> <Text onPress={()=>{ TestOJO.emitterO2J(); }} style={styles.himiTextStyle}>JS -> OC -> JS </Text> </View> ); } }; var styles = StyleSheet.create({ himiTextStyle:{ backgroundColor:'#eee', color:'#f00', fontSize:30, marginTop:70, }, });
下面是运行效果:(点击看动态图,主要看演示过程与控制台输出哦!)