reactnative|reactnative 桥接原生文件配置之获取设备号组件

本文原创首发于公众号:ReactNative开发圈,转载需注明出处。(有一些功能不一定能用,反正我用时undefined)

这次介绍的获取移动设备信息的组件名叫:react-native-device-info,兼容IOS和安卓双平台,可以获取设备ID、设备品牌、设备型号、IP以及APP版本号等信息。是一个应用很广泛的基础组件。
安装依赖
npm install --save react-native-device-info

注:如果React Naitve的版本大于0.47,那么需要使用>=0.11版本的react-native-device-info组件
自动连接原生项目
react-native link react-native-device-info

示例 参考官方文档:https://github.com/rebeccahughes/react-native-device-info#getdeviceid
有一个方法比较特殊就是isPinOrFingerprintSet方法,需要使用回调调用。代码如下:
import DeviceInfo from 'react-native-device-info'; DeviceInfo.isPinOrFingerprintSet()(isPinOrFingerprintSet => { if (!isPinOrFingerprintSet) { ... } }

直接集成有些方法会返回undefined
为了解决上述功能不行,那么怎么办呢?
很简单架设RN与原生的桥架来解决,以获取设备信息为例 react-native的文档的原生模块中可以看到清晰的代码 传送门
iOS桥接文件:
在ios文件夹中,添加一个nativeBridge文件夹(为了整理好文件,无实际作用),在该文件夹下添加桥接文件RNDeviceBridgeManager的.h和.m文件(这个文件名随便起,最好符合命名规范),前期工作就准备完毕了,现在开始文件配置,如下:
RNDeviceBridgeManager.h
#import #import #import // 不能去掉,否则会报莫名错误@interface RNDeviceBridgeManager : NSObject @end

RNDeviceBridgeManager.m
#import "RNDeviceBridgeManager.h" #import "sys/utsname.h" #import // 获取运营商信息 #import @implementation RNDeviceBridgeManagerRCT_EXPORT_MODULE(AppDeviceInfo) // 解决警告 - Module RNDeviceBridgeManager requires main queue setup since it overrides `constantsToExport` but doesn't implement `requiresMainQueueSetup`. - (NSDictionary *)constantsToExport{ NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; NSString *appName = [info objectForKey:@"CFBundleDisplayName"]; NSString *appVersion = [info objectForKey:@"CFBundleShortVersionString"]; CGRect rect = [[UIScreen mainScreen] bounds]; CGSize size = rect.size; CGFloat width = size.width; CGFloat height = size.height; NSArray *screenSize =@[@(width), @(height)]; CGFloat scale_screen = [UIScreen mainScreen].scale; NSArray *screenScale = @[@(width*scale_screen), @(height*scale_screen)]; CTTelephonyNetworkInfo *info_tel = [[CTTelephonyNetworkInfo alloc] init]; CTCarrier *carrier = info_tel.subscriberCellularProvider; NSDictionary *deviceInfo = @{ @"sysVersion": [[UIDevice currentDevice] systemVersion], // 手机系统版本 @"deviceId": [[[UIDevice currentDevice] identifierForVendor] UUIDString], // 设备唯一标识符 @"platformType": [[UIDevice currentDevice] systemName], // 设备名称 @"phoneName": [[UIDevice currentDevice] name], // 手机别名: 用户定义的名称 @"phoneType": [self deviceVersion], // 手机型号 @"localPhoneModel": [[UIDevice currentDevice] localizedModel], // 地方型号(国际化区域名称) @"size_screen": screenSize, // 物理尺寸 @"scale_screen": screenScale, // 分辨率 @"carrierName": carrier != nil ? carrier.carrierName : nil, // 运营商 -- 模拟器无运营商要特别处理,不然会闪退,nil时返回值里不包含本条信息 @"appVersion": appVersion, // 当前应用软件版本比如:1.0.1 @"appBuild": [info objectForKey:@"CFBundleVersion"], // 当前应用版本Build值int类型 @"appName": appName, // app名称 }; return deviceInfo; }- (NSString *)deviceVersion{ // 需要#import "sys/utsname.h" struct utsname systemInfo; uname(&systemInfo); NSString * deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; //iPhone if ([deviceString isEqualToString:@"iPhone3,1"])return @"iPhone 4"; if ([deviceString isEqualToString:@"iPhone3,2"])return @"iPhone 4"; if ([deviceString isEqualToString:@"iPhone3,3"])return @"iPhone 4"; if ([deviceString isEqualToString:@"iPhone4,1"])return @"iPhone 4S"; if ([deviceString isEqualToString:@"iPhone5,1"])return @"iPhone 5"; if ([deviceString isEqualToString:@"iPhone5,2"])return @"iPhone 5 (GSM+CDMA)"; if ([deviceString isEqualToString:@"iPhone5,3"])return @"iPhone 5c (GSM)"; if ([deviceString isEqualToString:@"iPhone5,4"])return @"iPhone 5c (GSM+CDMA)"; if ([deviceString isEqualToString:@"iPhone6,1"])return @"iPhone 5s (GSM)"; if ([deviceString isEqualToString:@"iPhone6,2"])return @"iPhone 5s (GSM+CDMA)"; if ([deviceString isEqualToString:@"iPhone7,1"])return @"iPhone 6 Plus"; if ([deviceString isEqualToString:@"iPhone7,2"])return @"iPhone 6"; if ([deviceString isEqualToString:@"iPhone8,1"])return @"iPhone 6s"; if ([deviceString isEqualToString:@"iPhone8,2"])return @"iPhone 6s Plus"; if ([deviceString isEqualToString:@"iPhone8,4"])return @"iPhone SE"; // 日行两款手机型号均为日本独占,可能使用索尼FeliCa支付方案而不是苹果支付 if ([deviceString isEqualToString:@"iPhone9,1"])return @"国行、日版、港行iPhone 7"; if ([deviceString isEqualToString:@"iPhone9,2"])return @"港行、国行iPhone 7 Plus"; if ([deviceString isEqualToString:@"iPhone9,3"])return @"美版、台版iPhone 7"; if ([deviceString isEqualToString:@"iPhone9,4"])return @"美版、台版iPhone 7 Plus"; if ([deviceString isEqualToString:@"iPhone10,1"])return @"iPhone_8"; if ([deviceString isEqualToString:@"iPhone10,4"])return @"iPhone_8"; if ([deviceString isEqualToString:@"iPhone10,2"])return @"iPhone_8_Plus"; if ([deviceString isEqualToString:@"iPhone10,5"])return @"iPhone_8_Plus"; if ([deviceString isEqualToString:@"iPhone10,3"])return @"iPhone_X"; if ([deviceString isEqualToString:@"iPhone10,6"])return @"iPhone_X"; if ([deviceString isEqualToString:@"iPod1,1"])return @"iPod Touch 1G"; if ([deviceString isEqualToString:@"iPod2,1"])return @"iPod Touch 2G"; if ([deviceString isEqualToString:@"iPod3,1"])return @"iPod Touch 3G"; if ([deviceString isEqualToString:@"iPod4,1"])return @"iPod Touch 4G"; if ([deviceString isEqualToString:@"iPod5,1"])return @"iPod Touch (5 Gen)"; if ([deviceString isEqualToString:@"iPad1,1"])return @"iPad"; if ([deviceString isEqualToString:@"iPad1,2"])return @"iPad 3G"; if ([deviceString isEqualToString:@"iPad2,1"])return @"iPad 2 (WiFi)"; if ([deviceString isEqualToString:@"iPad2,2"])return @"iPad 2"; if ([deviceString isEqualToString:@"iPad2,3"])return @"iPad 2 (CDMA)"; if ([deviceString isEqualToString:@"iPad2,4"])return @"iPad 2"; if ([deviceString isEqualToString:@"iPad2,5"])return @"iPad Mini (WiFi)"; if ([deviceString isEqualToString:@"iPad2,6"])return @"iPad Mini"; if ([deviceString isEqualToString:@"iPad2,7"])return @"iPad Mini (GSM+CDMA)"; if ([deviceString isEqualToString:@"iPad3,1"])return @"iPad 3 (WiFi)"; if ([deviceString isEqualToString:@"iPad3,2"])return @"iPad 3 (GSM+CDMA)"; if ([deviceString isEqualToString:@"iPad3,3"])return @"iPad 3"; if ([deviceString isEqualToString:@"iPad3,4"])return @"iPad 4 (WiFi)"; if ([deviceString isEqualToString:@"iPad3,5"])return @"iPad 4"; if ([deviceString isEqualToString:@"iPad3,6"])return @"iPad 4 (GSM+CDMA)"; if ([deviceString isEqualToString:@"iPad4,1"])return @"iPad Air (WiFi)"; if ([deviceString isEqualToString:@"iPad4,2"])return @"iPad Air (Cellular)"; if ([deviceString isEqualToString:@"iPad4,4"])return @"iPad Mini 2 (WiFi)"; if ([deviceString isEqualToString:@"iPad4,5"])return @"iPad Mini 2 (Cellular)"; if ([deviceString isEqualToString:@"iPad4,6"])return @"iPad Mini 2"; if ([deviceString isEqualToString:@"iPad4,7"])return @"iPad Mini 3"; if ([deviceString isEqualToString:@"iPad4,8"])return @"iPad Mini 3"; if ([deviceString isEqualToString:@"iPad4,9"])return @"iPad Mini 3"; if ([deviceString isEqualToString:@"iPad5,1"])return @"iPad Mini 4 (WiFi)"; if ([deviceString isEqualToString:@"iPad5,2"])return @"iPad Mini 4 (LTE)"; if ([deviceString isEqualToString:@"iPad5,3"])return @"iPad Air 2"; if ([deviceString isEqualToString:@"iPad5,4"])return @"iPad Air 2"; if ([deviceString isEqualToString:@"iPad6,3"])return @"iPad Pro 9.7"; if ([deviceString isEqualToString:@"iPad6,4"])return @"iPad Pro 9.7"; if ([deviceString isEqualToString:@"iPad6,7"])return @"iPad Pro 12.9"; if ([deviceString isEqualToString:@"iPad6,8"])return @"iPad Pro 12.9"; if ([deviceString isEqualToString:@"AppleTV2,1"])return @"Apple TV 2"; if ([deviceString isEqualToString:@"AppleTV3,1"])return @"Apple TV 3"; if ([deviceString isEqualToString:@"AppleTV3,2"])return @"Apple TV 3"; if ([deviceString isEqualToString:@"AppleTV5,3"])return @"Apple TV 4"; if ([deviceString isEqualToString:@"i386"])return @"Simulator"; if ([deviceString isEqualToString:@"x86_64"])return @"Simulator"; return deviceString; } @end

文件配置说明: .h文件中#import 配置桥接文件必须导入这个RN桥接模块依赖,并遵循“RCTBridgeModule”协议;在.m文件中实现“RCTBridgeModule”协议。 以示例中导出设备信息的方法为例,RCT_EXPORT_MODULE(AppDeviceInfo)为“RCTBridgeModule”协议提供方法,即在RN中用AppDeviceInfo标识导出方法; 为了实现RCTBridgeModule协议,你的类需要包含RCT_EXPORT_MODULE()宏。这个宏也可以添加一个参数用来指定在Javascript中访问这个模块的名字。如果你不指定,默认就会使用这个Objective-C类的名字。其他宏的运用本文就不做赘述,要了解移步传送门
RN中调用桥接文件模块
import { NativeModules } from 'react-native' /** * 获取用户设备码 */ export function fetchDeviceId() { const AppInfo = NativeModules.AppDeviceInfo console.log('桥接文件返回的值', AppInfo) return AppInfo.deviceId }

以上iOS部分的桥接工作就算完成了,但是最新的react-native版本(0.56.0),会报黄色Warning,强迫症患者,解决办法如下:
// 在程序入口(即路由配偶文件) import { YellowBox, } from 'react-native' YellowBox.ignoreWarnings(['Module RNDeviceBridgeManager requires main queue setup since it overrides `constantsToExport` but doesn\'t implement `requiresMainQueueSetup`. In a future release React Native will default to initializing all native modules on a background thread unless explicitly opted-out of.'])

注意:上述()中的即是黄色警告内容了,但是遇到有 ' 的情况就尴尬了,比如doesn't这样会报错,需要对 ' 进行转义,在 ' 前加 \ 就好了; Android桥接文件:
本人对Android不太了解,所以不能提供同ios一样详细的解释,步骤请照抄,如果你了解安卓特性可自由发挥;
在android文件夹下,路径app/src/main/java/com/appname 文件夹下(appname为你的项目名)创建一个bridges文件夹,在该文件夹下添加桥接文件RNDeviceBridgeManager.java,同时在app/src/main/java/com/appname 文件夹下创建 “注册模块”文件RNReactPackage.java,开始配置文件,如下:
RNDeviceBridgeManager.java
// RNDeviceBridgeManager.javapackage com.reactnative.bridges; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; // 导出方法用 import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactMethod; import java.util.Map; import java.util.HashMap; public class RNDeviceBridgeManager extends ReactContextBaseJavaModule {private static final String REACT_NAME = "AppDeviceInfo"; // private static final String SYSTEM_VERSION = "sysVersion"; private static final String DEVICE_ID = "deviceId"; // private static final String PLATFORM_TYPE = "platformType"; // private static final String PHONE_TYPE = "phoneType"; // private static final String CARRIER_NAME = "carrierName"; // private static final String PACKAGE_NAME = "packageName"; // private static final String APP_VERSION = "appVersion"; // private static final String APP_NAME = "appName"; public RNDeviceBridgeManager(ReactApplicationContext reactContext) { super(reactContext); }@Override public String getName() { return REACT_NAME; }@Override public Map getConstants() { final Map constants = new HashMap<>(); constants.put(DEVICE_ID, "1234"); return constants; } }

RNReactPackage.java
// RNReactPackage.javapackage com.reactnative; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class RNReactPackage implements ReactPackage {@Override public List createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); }@Override public List createNativeModules( ReactApplicationContext reactContext) { List modules = new ArrayList<>(); modules.add(new RNDeviceBridgeManager(reactContext)); return modules; }}

然后可以在RN项目文件中调用Android桥接文件里的东西,测试上述'AppDeviceInfo'信息是否有打印,如有打印则可根据具体业务将上述注释掉的代码补齐,测试方式如下:
import { View, NativeModules, } from 'react-native class home extends React.Component { constructor(props) { super(props) this.state = {} }render() { console.log(NativeModules.AppDeviceInfo) return ( ) } }module.exports = home

综述,RN项目桥接调用iOS和Android原生方法的配置方法讲述完毕,谢谢观看;荆轲刺秦王!
另附:
获取Android设备唯一标识码:https://www.jianshu.com/p/2ceda3690a52
获取系统版本:
//需导入系统架包 import android.os.Build; constants.put(SYSTEM_VERSION, Build.VERSION.RELEASE); // 下面是拼接信息(可以不这样,看个人) constants.put(SYSTEM_VERSION, Build.MODEL+" Android "+Build.VERSION.RELEASE+" "+Build.VERSION.SDK_INT);

获取packageName包名:
// 需导入系统架包 import com.facebook.react.bridge.ReactApplicationContext; constants.put(PACKAGE_NAME,getReactApplicationContext().getPackageName());

获取app版本号:
// 需导入系统架包 import android.content.pm.PackageInfo; // import android.content.pm.PackageManager; PackageInfo info = getPackageInfo(); if(info != null){ constants.put(APP_VERSION,info.versionName); } private PackageInfo getPackageInfo(){ PackageManager manager = getReactApplicationContext().getPackageManager(); PackageInfo info = null; try{ info = manager.getPackageInfo(getReactApplicationContext().getPackageName(),0); return info; }catch (Exception e){ e.printStackTrace(); }finally { return info; } }

手机型号:
constants.put(PHONE_TYPE,Build.MODEL);

【reactnative|reactnative 桥接原生文件配置之获取设备号组件】平台和应用名称自己写固定的;
运营商信息,网上查找的资料全部不能用,不是缺少所需架包,就是一些方法未定义,尚未找到方法,所以直接不设置了;(吐槽一下:抄袭都不验证的一大堆,百度上全是这种,还排名置顶,害人不浅!)

    推荐阅读