原文博客地址: 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
協議, 並加上以下代碼併發
@synthesize bridge = _bridge;
複製代碼
最後不要忘記移除該通知app
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"urlChange" object:nil];
}
複製代碼
在JavaScript
中接受iOS
原生髮送的消息通知異步
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哈哈~
歡迎您掃一掃下面的微信公衆號,訂閱個人博客!