原文博客地址: www.titanjun.top/ReactNative…html
在ReactNative
開發中, 在JavaScript
語法沒法實現的時候會涉及到一些原生開發, 既然是混合開發就會涉及到一些iOS
和ReactNative
之間通信的問題, 這裏就涉及到兩種方式:react
RN
調用原生的方法, 給原生髮送數據RN
回傳數據, 或者給RN
發送通知JS
調用原生方法,最後由原生方法將結果回調到JS裏面react-native
是在原生的基礎上,將接口調用統一爲js
react-native
調起原生的能力很是重要在原生須要建立一個繼承自NSObject
的類(模塊)ios
#import <Foundation/Foundation.h> // 須要導入頭文件 #import <React/RCTBridgeModule.h> // 必須遵循RCTBridgeModule協議 @interface AppEventMoudle : NSObject <RCTBridgeModule> @end 複製代碼
在AppEventMoudle.m
文件件中須要導出改模塊, 並將建立的方法導出web
#import "AppEventMoudle.h" #import <React/RCTBridge.h> @implementation AppEventMoudle // 導出橋接模塊, 參數傳空或者當前class的類名 // 參數若爲空, 默認模塊名爲當前class類名即AppEventMoudle RCT_EXPORT_MODULE(AppEventMoudle); // 帶有參數 RCT_EXPORT_METHOD(OpenView:(NSDictionary *)params){ // 由於是顯示頁面,因此讓原生接口運行在主線程 dispatch_async(dispatch_get_main_queue(), ^{ // 在這裏能夠寫須要原生處理的UI或者邏輯 NSLog(@"params = %@", params); }); } /// 帶有回調 RCT_EXPORT_METHOD(OpenView:(NSDictionary *)params, callback:(RCTResponseSenderBlock)callback){ // 由於是顯示頁面,因此讓原生接口運行在主線程 dispatch_async(dispatch_get_main_queue(), ^{ // 在這裏能夠寫須要原生處理的UI或者邏輯 NSLog(@"params = %@", params); if (callback) { callback(@[params]); } }); } 複製代碼
上面代碼須要注意的是react-native
Javascript
的方法返回值類型必須是void
React Native
的橋接操做是異步的,在queue
裏面異步執行,因此若是要返回結果給Javascript
,就必須經過回調或者觸發事件來進行iOS
端就是經過block
來回調的RCTBridge
能夠說是一個封裝類,封裝了RCTCxxBridge
RCTModuleClasses
: 主要儲存的是咱們註冊的module
, 全部用宏RCT_EXPORT_MODULE()
註冊的module
都會存入這個變量.RCTGetModuleClasses
: 獲取RCTModuleClasses
裏面全部註冊的module
類RCTBridgeModuleNameForClass
: 從一個類獲取這個類的名字RCTVerifyAllModulesExported
: 驗證咱們所寫的全部遵照RCTBridgeModule
協議的類是否都在咱們的管理中typedef void (^RCTResponseSenderBlock)(NSArray *response); 複製代碼
RCTResponseSenderBlock
是RCTBridgeModule
裏面提供的block
block
接受一個數組參數, 表明原生方法的返回結果queue
裏面異步執行的queue
是不夠的,咱們有時候須要指定模塊全部任務執行所在的queue
RCT_EXPORT_METHOD(OpenView:(NSDictionary *)params){ // 由於是顯示頁面,因此讓原生接口運行在主線程 dispatch_async(dispatch_get_main_queue(), ^{ // 在這裏能夠寫須要原生處理的UI或者邏輯 NSLog(@"params = %@", params); }); } 複製代碼
H5
頁面, 經過原生的Webview
實現, 而且監聽url
的變化, 並通知js
作相關操做url
變化的時候, 給JavaScript
發送監聽通知RCTResponseSenderBlock
進行回調, block
回調只能執行一次, 並不能不斷的執行WebViewController
的控制器, 在該控制器內添加UIWebView
的UI和邏輯的實現UIWebViewDelegate
的協議方法中監聽webview
的url
的變化, 併發送通知- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *url= request.URL.absoluteString; if (url && ![url isEqualToString:@""]) { // 發送通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"urlChange" object:url]; } [self clickAction:url]; return YES; } 複製代碼
須要在js
調用的方法中接受上述代碼中發送的通知, 以下數組
RCT_EXPORT_METHOD(OpenWebView:(NSDictionary *)params){ dispatch_async(dispatch_get_main_queue(), ^{ // 接受通知監聽 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(urlChange:) name:@"urlChange" object:nil]; WebViewController *webView = [WebViewController new]; webView.params = params; UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:webView]; UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController; [rootVC presentViewController:navi animated:YES completion:nil]; }); } 複製代碼
實現監聽方法, 並給JavaScript
發送消息通知微信
- (void)urlChange:(NSNotification *)notification{ [self.bridge.eventDispatcher sendAppEventWithName:@"NativeWebView" body:@{@"url":(NSString *)notification.object}]; } 複製代碼
要獲取self.bridge
屬性, 須要遵循RCTBridgeModule
協議, 並加上以下代碼markdown
@synthesize bridge = _bridge; 複製代碼
最後不要忘記移除該通知併發
- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self name:@"urlChange" object:nil]; } 複製代碼
在JavaScript
中接受iOS
原生髮送的消息通知app
this.webViewListener = NativeAppEventEmitter.addListener('NativeWebView', message => { this.handleMessageFromNative(message) }) // 並在對應的位置銷燬便可 this.webViewListener && this.webViewListener.remove() this.webViewListener = null 複製代碼
js
調用iOS
原生代碼後, 用iOS
原生在給js
發送事件監聽iOS
原生主動給js
發送監聽事件呢, 相似場景: 好比在AppDelegate
中給js
發送事件通知有改如何實現APP
進入後臺和APP
從後臺進入前臺的事件, 並在JavaScript
中作相關操做_bridge
, 並遵循RCTBridgeModule
協議, 就可使用下面代碼發送監聽事件了, 加斷點能夠發現, 下面獲取的self.bridge
爲nil
-(void)applicationDidEnterBackground:(UIApplication *)application { // 這裏的self.bridge爲nil [self.bridge.eventDispatcher sendAppEventWithName:@"NativeWebView" body:@{@"url":(NSString *)notification.object}]; } 複製代碼
下面先介紹一個消息監聽的實例類
RCTEventEmitter
是一個基類, 用於發出JavaScript
須要監聽的事件, 提供了一下屬性和方法
@interface RCTEventEmitter : NSObject <RCTBridgeModule> @property (nonatomic, weak) RCTBridge *bridge; // 返回你將要發送的消息的name, 若是有未添加的, 運行時將會報錯 - (NSArray<NSString *> *)supportedEvents; // 用於發送消息事件 - (void)sendEventWithName:(NSString *)name body:(id)body; // 在子類中重寫此方法, 用於發送/移除消息通知 - (void)startObserving; - (void)stopObserving; // 添加監聽和移除監聽 - (void)addListener:(NSString *)eventName; - (void)removeListeners:(double)count; @end 複製代碼
具體的使用示例, 可繼續向下看
建立一個繼承自RCTEventEmitter
的類, 並遵循協議<RCTBridgeModule>
#import <Foundation/Foundation.h> #import <React/RCTBridgeModule.h> NS_ASSUME_NONNULL_BEGIN @interface AppEventManager : RCTEventEmitter <RCTBridgeModule> @end NS_ASSUME_NONNULL_END 複製代碼
再具體的iOS
原生代碼中發送消息通知
#import "AppEventManager.h" @implementation AppEventManager // 導出該模塊 RCT_EXPORT_MODULE(); // 返回sendEventWithName中監聽的name, 若是有監聽, 可是爲在該方法中添加的, 運行時會報錯 - (NSArray<NSString *> *)supportedEvents { return @[@"DidEnterBackground", @"DidBecomeActive"]; } // 添加觀察者事件, 重寫該方法中, 並在該方法中接受消息通知 - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:@"DidEnterBackground" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:@"DidBecomeActive" object:nil]; } // 移除觀察者 - (void)stopObserving { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)applicationDidEnterBackground:(NSNotification *)notification{ // 在此處向JavaScript發送監聽事件 [self sendEventWithName:@"DidEnterBackground" body: notification.object]; } - (void)applicationDidBecomeActive:(NSNotification *)notification{ // 在此處向JavaScript發送監聽事件 [self sendEventWithName:@"DidBecomeActive" body: notification.object]; } 複製代碼
注意
RCT_EXPORT_MODULE()
聲明該類是EXPORT_MODULE
, 那麼該類的實例已經建立好了alloc
或 new
), 會致使,ReactNative
不能正確識別該類的實例在ReactNative
中引用該模塊, 並添加對對應事件的監聽便可
先導出iOS
原生定義的模塊
// AppEventManager爲原生中建立的類名 const appEventMan = new NativeEventEmitter(NativeModules.AppEventManager) 複製代碼
使用appEventMan
在對應的地方添加監聽便可
this.didEnterBackground = appEventMan.addListener('DidEnterBackground', () => { console.log(`APP開始進入後臺---------------`) }) this.didBecomeActive = appEventMan.addListener('DidBecomeActive', () => { console.log(`APP開始從後臺進入前臺----------`) }) 複製代碼
可是也不要忘記在對應的地方移除該監聽
componentWillUnmount () { this.didEnterBackground && this.didEnterBackground.remove() this.didEnterBackground = null this.didBecomeActive && this.didBecomeActive.remove() this.didBecomeActive = null } 複製代碼
至此, 在ReactNative
中JavaScript
和iOS
原生的交互基本就結束了, O(∩_∩)O哈哈~
歡迎您掃一掃下面的微信公衆號,訂閱個人博客!