【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)與IOS(OBJECT-C)交互通信

一用到跨平臺的引擎必然要有引擎與各平臺原生進行交互通信的需要。那麼Himi先講解React Native與iOS之間的通信交互。

       本篇主要分爲兩部分講解:(關於其中講解的OC語法等不介紹,不懂的請自行學習)

       1. React Native 訪問iOS 

       2. iOS訪問React Native

 

    一:React Native 訪問iOS

1. 我們想要JS調用OC函數,就要實現一個“RCTBridgeModule”協議的Objective-C類

所以首先我們先創建一個oc新類,  Himi這裏起名爲:TestOJO  (O: object-c, J: javaScript )

2. TestOJO.h

#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
 
@interface TestOJO : NSObject  <RCTBridgeModule>
 
@end


引入:#import “RCTBridgeModule.h”   且使用 <RCTBridgeModule> 接口,

3. 爲了實現RCTBridgeModule協議,類需要包含RCT_EXPORT_MODULE()宏(這個宏也可以添加一個參數用來指定在Javascript中訪問這個模塊的名字。如果你不指定,默認就會使用這個Objective-C類的名字。)

4. 在TestOJO.m中添加如下:

RCT_EXPORT_MODULE();
//橋接到Javascript的方法返回值類型必須是void。React Native的橋接操作是異步的,所以要返回結果給Javascript,必須通過回調或者觸發事件來進行
RCT_EXPORT_METHOD(j2oFun1:(NSString *)dataString dateNumber:(int)dateNumber)
{
    NSLog(@"js call iOS function j2oFun1\n dataString: %@ |dateNumber :%d",dataString,dateNumber);
}


想要將oc的函數導出給js進行調用,那麼就需要進行聲明。聲明通過RCT_EXPORT_METHOD()宏來實現:

j2oFun1:函數名,後續是兩個參數,分別是NSString 和 int 類型數據。

調用成功後,我們輸出這兩個傳來的值到控制檯。

注意:Javascript調用的OC函數,此函數返回值類型必須是void。由於React Native的橋接操作是異步的,所以要返回結果給Javascript,必須通過回調參數進行 後續詳細講解。

從js傳來的參數我們可以依靠自動類型轉換的特性,跳過手動的類型轉換(RCTConvert,下面詳細介紹),在定義函數參數類型時,直接寫上對應想要的數據類型,例如NSData等。

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);(調用模塊中的對應函數,且將參數進行傳入)

6. 我們來看一段複雜的數據通信

OC 代碼段(導出函數):

#import "RCTConvert.h"
RCT_EXPORT_METHOD(j2oFun2:(NSDictionary *)details)
{
  NSString *name = [RCTConvert NSString:details[@"name"]];
  NSNumber *age = [RCTConvert NSNumber:details[@"age"]];
  NSArray * array =[RCTConvert NSArray:details[@"array"]];
  NSLog(@"js call iOS function j2oFun2\n name: %@ | age :%@", name, [age stringValue]);
  
  for (int i = 0; i<[array count]; i++) {
    NSLog(@"array: 第%d個元素:%@",i,array[i]);
  }
}

需要注意的是,引入了”RCTConvert”類,作用:

RCTConvert提供了一系列輔助函數,用來接收一個JSON值並轉換到原生Objective-C類型或類。

JS代碼段:(調用OC函數)

TestOJO.j2oFun2({
    name:'Himi',
    age:12,
    array:[
          'hi,Himi','i,m','a array!'
    ]
 });


7. 我們下面來利用回調參數來得到訪問OC的函數得到其返回值

RCT_EXPORT_METHOD(j2oCallbackEvent:(NSString *)jsString callback:(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]);
   }
});


二: iOS訪問React Native

1.  我們如果想要OC訪問JS,給JavaScript發送事件通知,我們需要使用RCTEventDispatcher的函數,與RCTBridge的實例

因此我們需要先做準備,TestOJO.h:

#import "RCTEventDispatcher.h"
@synthesize bridge = _bridge;

bridge: 是RCTBridge 的實例,且在我們使用的接口 RCTBridgeModule中。

OC訪問JS的代碼段:

[self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:@{@"name":@"Himi",@"age": @12}];

第一個參數:事件名

第二個參數(body):傳入的參數

其中@{}是定義不可變的字典的快捷實例方式,因此我們也可以改成如下形式:

NSDictionary * direct =@{@"name": @"Himi",@"age": @12};
    [self.bridge.eventDispatcher sendAppEventWithName:@"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 *)dataString dateNumber:(int)dateNumber)
{
    NSLog(@"js call iOS function j2oFun1\n dataString: %@ |dateNumber :%d",dataString,dateNumber);
}
RCT_EXPORT_METHOD(j2oFun2:(NSDictionary *)details)
{
  NSString *name = [RCTConvert NSString:details[@"name"]];
  NSNumber *age = [RCTConvert NSNumber:details[@"age"]];
  NSArray * array =[RCTConvert NSArray:details[@"array"]];
  NSLog(@"js call iOS function j2oFun2\n name: %@ | age :%@", name, [age stringValue]);
  
  for (int i = 0; i<[array count]; i++) {
    NSLog(@"array: 第%d個元素:%@",i,array[i]);
  }
  
}
//帶回調函數 RCTResponseSenderBlock ,提供將返回值傳回給js
//RCTResponseSenderBlock 只接受一個參數->傳遞給JavaScript回調函數的參數數組
RCT_EXPORT_METHOD(j2oCallbackEvent:(NSString *)jsString callback:(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.eventDispatcher sendAppEventWithName:@"eventName" body:direct];
  
  //  [self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:@{@"name":@"Himi",@"age": @12}];
  
}
@end


Main.js:

import React, { 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 (
       	<View style={{flex: 1, alignItems: 'center'}}>
         <Text style={styles.himiTextStyle}>Himi React Native 系列教程</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,
  },
});

下面是運行效果:(點擊看動態圖,主要看演示過程與控制檯輸出哦!)

user918

注意:

1.點擊JS->OC 後,會調用三個函數哦

2.點擊JS->OC->JS, 先是通過JS->OC的臨時函數,觸發OC->JS的過程!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章