原文博客地址: 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哈哈~
欢迎您扫一扫下面的微信公众号,订阅个人博客!