React Native填坑之旅--与Native通信之iOS篇
终于开始新一篇的填坑之旅了。RN厉害的一个地方就是RN可以和Native组件通信。这个Native组件包括native的库和自定义视图,我们今天主要涉及的内容是native库方面的知识。自定义视图的使用会在后面讲到。 坑是什么样的坑主要的是遇到一个业务需求,需要检测当前应用的版本是什么。需要返回当前的版本号和build数。 主要的需求在native来说非常简单: NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey]; 两句分别获得了版本号和build数。 开始填坑填坑其实也是意外的简单。当然,我们不准备把这个代码作为库发布到npm上给别人用,所以复杂度自然降低了不少。 首先、在Xcode里创建 在RNUpgrade.h头文件中,添加 #import <Foundation/Foundation.h> #import <React/RCTBridgeModule.h> // #import "RCTBridgeModule.h" 如果RN版本低于0.40则使用这个 @interface RNUpgrade : NSObject<RCTBridgeModule> @end 之后对于头文件就可以什么都不用管了。至少对于暴露接口这件事是这样的。 下面就来看源文件吧。看文档,要暴露native方法就必须在源文件里包含一个宏的调用,这个宏是: #import "RNUpgrade.h" #import <React/RCTUtils.h> // #import "RCTUtils.h" RN版本 < 0.40时使用这个 #import "AppDelegate.h" NSString *const RNUPGRADE_ERROR_DOMAIN = @"Upgrade info error"; @implementation RNUpgrade RCT_EXPORT_MODULE(); @end 那么如何来暴露出一个方法呢?使用 RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location) { RCTLogInfo(@"Pretending to create an event %@ at %@",name,location); } 宏 RCT_EXPORT_METHOD(getCurrentInfo) { @try { NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey]; return @{@"versionName": version,@"versionCode": build} } @catch (NSException *exception) { //Log error info... } } 但是,如何返回字典呢?直接return?接着差文档。 暴露给RN的方法是不能直接返回任何东西的。因为RN的调用时异步的,所以只能使用回调的方式,或者触发事件的方式实现返回值。 回调!看个官网的例子: RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = ... callback(@[[NSNull null],events]); } 好的,回调就说到这里了。因为笔者的项目已经上了 RCT_EXPORT_METHOD(getCurrentInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { @try { NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey]; resolve(@{@"versionName": version,@"versionCode": build}); } @catch (NSException *exception) { NSError *error = [NSError errorWithDomain:RNUPGRADE_ERROR_DOMAIN code:1 userInfo: exception.userInfo]; reject(exception.name,exception.reason,error); } } 于是,这样就可以返回一个 在RN的项目里调用这个方法: // 首先通过`NativeModules`接收暴露的native模块。 import { NativeModules } from "react-native" const upgrade = NativeModules.RNUpgrade // 方法调用 const ret = await Promise.all([upgrade.getCurrentInfo(),upgrade.getUpgradeInfo()]) 没错,模块还有另外一个native方法。这个native方法也返回一个 返回声明相同的native方法其实在native模块里很多方法的声明都是一模一样的: RCT_REMAP_METHOD(getCurrentInfo,resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { @try { NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey]; resolve(@{@"versionName": version,error); } } 基本上在项目里如何暴露一个native方法给RN的js调用非常简单,就如上面所述一样。
重要的一点:线程在文档中有这么一点:多线程。千万不要根据RN实现的一些细节就假设你的模块运行在某某线程上。官网也说了,这个是会变的。如果你要确定你的代码运行在什么线程上,通过方法 注意:指定的methodQueue会被你模块里的所有方法共享。 如果运行在主线程上: - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } 如果运行在自己创建的线程上: - (dispatch_queue_t)methodQueue { return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue",DISPATCH_QUEUE_SERIAL); } 如果模块里只有小部分代码运行在其他的线程上,可以使用native里传统的方法 RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{ // 在这里执行长时间的操作 ... // 你可以在任何线程/队列中执行回调函数 callback(@[...]); }); } 而且:**methodQueue 省心省力! 填坑完毕!(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |