【JAVA】Device设备信息JSON,随机生成算法
-
【声明】仅供学习交流使用,切勿用于非法用途
设备信息工厂类
generateDeviceInfo方法用来生成设备信息
import com.google.common.hash.Hashing; import com.utils.MT; import com.utils.ObjectUtil; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; public class DeviceFactory { // 使用SimpleDateFormat格式化输出,生成yyMMdd格式的字符串 private static final SimpleDateFormat format = new SimpleDateFormat("yyMMdd"); public static DeviceInfo generateDeviceInfo() { return generateDeviceInfo(IMEI.China); } /** * @param imei 设备ID * @return 设备信息,可通过toSting转换器JSON字符串 */ public static DeviceInfo generateDeviceInfo(IMEI imei) { MT mt = new MT(); DeviceInfo.Data data = new DeviceInfo.Data(); DeviceInfo.Display[] displays = DeviceInfo.Display.values(); DeviceInfo.Display display = displays[(int) (mt.random() % displays.length)]; DeviceInfo.SimInfo[] simInfos = DeviceInfo.SimInfo.values(); String imeiUUID = imei.generateIMEI(); // - display:设备显示屏参数,如:OPR1.170623.027 String displaysName = display.name() + '.' + format.format(new Date(mt.random() % 94608000_000L + (System.currentTimeMillis()) - 103248000_000L)) + '.' + String.format("%03d", (int) (mt.random() % 1000)); // - product:设备产品的名称,尽量不要太长 String product = ObjectUtil.isNone(imei.product()) ? Long.toString((long) (mt.random() % Math.pow(36, 4)), 36) : imei.product(); // - device:设备参数,如:sagit String device = Long.toString((long) (mt.random() % Math.pow(36, 4)), 36); // - board:设备主板,如:msm8998 String board = Long.toString((long) (mt.random() % Math.pow(36, 6)), 36); // - brand:系统定制商,如:Xiaomi String brand = ObjectUtil.isNone(imei.brand()) ? Long.toString((long) (mt.random() % Math.pow(36, 6)), 36) : imei.brand(); // - model:设备型号,如:MI 6 String model = ObjectUtil.isNone(imei.model()) ? Long.toString((long) (mt.random() % Math.pow(36, 8)), 36) : imei.model(); // - bootloader:系统启动程序版本号,可填unknown data.setDisplay(displaysName).setProduct(product).setDevice(device).setBoard(board).setBrand(brand).setModel(model); // - version:Android系统版本信息,包括版本号、发布版本和代号 DeviceInfo.Data.Version version = new DeviceInfo.Data.Version(); version.setIncremental(String.format("%07d", (int) (mt.random() % 10000000))); version.setRelease(display.version()); version.setCodename(Math.random() >= 0.5 ? "REL" : (Math.random() >= 0.5 ? "Q" : "P")); // - fingerprint:设备的唯一标识符,由设备品牌、设备型号、设备版本号、设备固件版本号和设备签名密钥等信息组成。设备指纹,唯一识别码,如:Xiaomi/sagit/sagit:8.0.0/OPR1.170623.027/V9.5.3.0.OCACNFA:user/release-keys String fingerprint = brand + "/" + device + "/" + board + ":" + version.getRelease() + "/" + display + "/" + product + ":user/" + (Math.random() > 0.001 ? "release-keys" : " test-keys"); // - bootId:UUID格式16位MD5,如:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX String bootId = String.format("%08x-%04x-4%03x-y%03x-%012x", (int) (mt.random() % 0x100000000L), (int) (mt.random() % 0x10000L), (int) (mt.random() % 0x1000L), (int) (mt.random() % 0x1000L) + 0x8000, (long) (mt.random() % 0x10000000000000L)).toUpperCase(); // - procVersion:系统版本,系统中/proc/version的值,如Linux version 4.9.0-9-amd64 (debian-kernel@lists.debian.org) String procVersion = "Linux version " + (int) (mt.random() % 256) + '.' + (int) (mt.random() % 256) + '.' + (int) (mt.random() % 256) + '-' + (Math.random() >= 0.5 ? "generic" : (Math.random() >= 0.5 ? "kvm" : "x86_64")) + " (" + Long.toString((long) (mt.random() % Math.pow(36, 8)), 36) + '@' + (int) (mt.random() % 256) + '.' + (int) (mt.random() % 256) + '.' + (int) (mt.random() % 256) + '.' + (int) (mt.random() % 256) + ')'; // - baseBand:基带版本 data.setBootloader("unknown").setFingerprint(fingerprint).setBootId(bootId).setProcVersion(procVersion).setBaseBand(""); data.setVersion(version); // - simInfo:SIM卡信息,可填unknown // - osType:系统类型,填android data.setSimInfo(simInfos[(int) (mt.random() % simInfos.length)].name().replace('_', ' ')).setOsType("android"); long random = mt.random() & 0xFF_FF_FF_FF_FF_FFL; // - macAddress:MAC地址:XX-XX-XX-XX-XX-XX String macAddress = String.format( "%02x:%02x:%02x:%02x:%02x:%02x", random >> 40 & 0xFF, random >> 32 & 0xFF, random >> 24 & 0xFF, random >> 16 & 0xFF, random >> 8 & 0xFF, random & 0xFF); random = mt.random() & 0xFF_FF_FF_FF_FF_FFL; // - wifiBSSID:wifi mac, 填02:00:00:00:00:00, 安卓新版本不可获取 String wifiBSSID = String.format( "%02x:%02x:%02x:%02x:%02x:%02x", random >> 40 & 0xFF, random >> 32 & 0xFF, random >> 24 & 0xFF, random >> 16 & 0xFF, random >> 8 & 0xFF, random & 0xFF); String imsiMd5 = Hashing.md5().hashString(imeiUUID, StandardCharsets.UTF_8).toString(); // - wifiSSID:wifi名称,随意 // - imsiMd5:国际移动用户识别码MD5,随便给个16位MD5即可 // - imei:IMEI手机序列号,具有校验规则 // - apn:apn, 填wifi data.setMacAddress(macAddress).setWifiBSSID(wifiBSSID).setWifiSSID("<unknown ssid>") .setImsiMd5(imsiMd5).setImei(imeiUUID).setApn("wifi"); return new DeviceInfo().setDeviceInfoVersion(2).setData(data); } }
设备信息类
可由设备信息工厂(DeviceFactory)生成,toString方法可以将【设备信息实例】转换为【JSON String】
public class DeviceInfo { private int deviceInfoVersion; private Data data; public enum Display { OPR1("8"), OPM1("8"), PPR1("9"), PQ1A("9"), QPR1("10"), QQ1A("10"), RPR1("11"), RQ2A("11"); private final String version; Display(String version) { this.version = version; } public String version() { return version; } } public enum Brand {Xiaomi, Samsung} public enum SimInfo {China_Mobile, China_Unicom, China_Telecom} public int getDeviceInfoVersion() { return deviceInfoVersion; } public DeviceInfo setDeviceInfoVersion(int deviceInfoVersion) { this.deviceInfoVersion = deviceInfoVersion; return this; } public Data getData() { return data; } public DeviceInfo setData(Data data) { this.data = data; return this; } public static class Data { private String display, product, device, board, brand, model; private String bootloader, fingerprint, bootId, procVersion, baseBand; private Version version; private String simInfo, osType, macAddress, wifiBSSID, wifiSSID, imsiMd5, imei, apn; public String getDisplay() { return display; } public Data setDisplay(String display) { this.display = display; return this; } public String getProduct() { return product; } public Data setProduct(String product) { this.product = product; return this; } public String getDevice() { return device; } public Data setDevice(String device) { this.device = device; return this; } public String getBoard() { return board; } public Data setBoard(String board) { this.board = board; return this; } public String getBrand() { return brand; } public Data setBrand(String brand) { this.brand = brand; return this; } public String getModel() { return model; } public Data setModel(String model) { this.model = model; return this; } public String getBootloader() { return bootloader; } public Data setBootloader(String bootloader) { this.bootloader = bootloader; return this; } public String getFingerprint() { return fingerprint; } public Data setFingerprint(String fingerprint) { this.fingerprint = fingerprint; return this; } public String getBootId() { return bootId; } public Data setBootId(String bootId) { this.bootId = bootId; return this; } public String getProcVersion() { return procVersion; } public Data setProcVersion(String procVersion) { this.procVersion = procVersion; return this; } public String getBaseBand() { return baseBand; } public Data setBaseBand(String baseBand) { this.baseBand = baseBand; return this; } public Version getVersion() { return version; } public Data setVersion(Version version) { this.version = version; return this; } public String getSimInfo() { return simInfo; } public Data setSimInfo(String simInfo) { this.simInfo = simInfo; return this; } public String getOsType() { return osType; } public Data setOsType(String osType) { this.osType = osType; return this; } public String getMacAddress() { return macAddress; } public Data setMacAddress(String macAddress) { this.macAddress = macAddress; return this; } public String getWifiBSSID() { return wifiBSSID; } public Data setWifiBSSID(String wifiBSSID) { this.wifiBSSID = wifiBSSID; return this; } public String getWifiSSID() { return wifiSSID; } public Data setWifiSSID(String wifiSSID) { this.wifiSSID = wifiSSID; return this; } public String getImsiMd5() { return imsiMd5; } public Data setImsiMd5(String imsiMd5) { this.imsiMd5 = imsiMd5; return this; } public String getImei() { return imei; } public Data setImei(String imei) { this.imei = imei; return this; } public String getApn() { return apn; } public Data setApn(String apn) { this.apn = apn; return this; } public static class Version { private String incremental, release, codename; public String getIncremental() { return incremental; } public Version setIncremental(String incremental) { this.incremental = incremental; return this; } public String getRelease() { return release; } public Version setRelease(String release) { this.release = release; return this; } public String getCodename() { return codename; } public Version setCodename(String codename) { this.codename = codename; return this; } @Override public String toString() { return "{\n \"incremental\": \"" + incremental + '\"' + ",\n \"release\": \"" + release + '\"' + ",\n \"codename\": \"" + codename + "\"\n }"; } } @Override public String toString() { return "{\n \"display\": \"" + display + '\"' + ",\n \"product\": \"" + product + '\"' + ",\n \"device\" :\"" + device + '\"' + ",\n \"board\": \"" + board + '\"' + ",\n \"brand\": \"" + brand + '\"' + ",\n \"model\": \"" + model + '\"' + ",\n \"bootloader\": \"" + bootloader + '\"' + ",\n \"fingerprint\": \"" + fingerprint + '\"' + ",\n \"bootId\": \"" + bootId + '\"' + ",\n \"procVersion\": \"" + procVersion + '\"' + ",\n \"baseBand\": \"" + baseBand + '\"' + ",\n \"version\": " + version + ",\n \"simInfo\": \"" + simInfo + '\"' + ",\n \"osType\": \"" + osType + '\"' + ",\n \"macAddress\": \"" + macAddress + '\"' + ",\n \"wifiBSSID\": \"" + wifiBSSID + '\"' + ",\n \"wifiSSID\": \"" + wifiSSID + '\"' + ",\n \"imsiMd5\": \"" + imsiMd5 + '\"' + ",\n \"imei\": \"" + imei + '\"' + ",\n \"apn\": \"" + apn + "\"\n }"; } } @Override public String toString() { return "{\n \"deviceInfoVersion\": " + deviceInfoVersion + ",\n \"data\": " + data + "\n}"; } }
IMEI生成器
用于生成IMEI-UUID
import com.utils.MT; import com.utils.ObjectUtil; public enum IMEI { China("86", null, null, null), // 8+(1+2)=11 Xiaomi_Redmi_K40("86307405", "Redmi_K40", "Xiaomi", "M201211AC"); // Type Allocation Code (TAC) private final String tac, product, brand, model; private final MT mt = new MT(); IMEI(String tac, String product, String brand, String model) { this.tac = tac; this.product = product; this.brand = brand; this.model = model; } public String value() { return tac; } public String product() { return product; } public String brand() { return brand; } public String model() { return model; } /** * 生成随机的IMEI号码 * * @return 生成的IMEI号码 */ public String generateIMEI() { // 计算TAC后缀长度 int tacSuffixLength = (8 - tac.length()); // 随机得到TAC后缀 String tacSuffix = ""; if (tacSuffixLength > 0) { int intTacSuffix = (int) (mt.random() % Math.pow(10, tacSuffixLength)); intTacSuffix = intTacSuffix / 100 * 100 + intTacSuffix % 10; tacSuffix = String.format("%0" + tacSuffixLength + 'd', intTacSuffix); } // 获取完整的TAC StringBuilder payload = new StringBuilder(tac + tacSuffix); // 计算出TAC的sum,TAC长度一定是8,所以不需要判断长度为奇数的情况 int sum = 0; for (int i = payload.length() - 1; i >= 0; i -= 2) { int digit = (payload.charAt(i) - '0') * 2; if (digit > 9) digit = digit / 10 + digit % 10; sum += digit + payload.charAt(i - 1) - '0'; } // 随机得到SNR序列号,长度一定是5,一定是奇数,计算到sum内 char[] snr = ObjectUtil.numberToChars(mt.random() % 90000 + 10000); for (int i = snr.length - 1; i >= 1; i -= 2) { int digit = (snr[i] - '0') * 2; if (digit > 9) digit = digit / 10 + digit % 10; sum += digit + snr[i - 1] - '0'; } { int digit = (snr[0] - '0') * 2; if (digit > 9) digit = digit / 10 + digit % 10; sum += digit; } // 如果sum是整10倍数,一定是无效的序列号,需要进行处理 if (sum % 10 == 0) return payload.append('1').append(new String(snr)).append(10 - (sum + 1) % 10).toString(); else return payload.append('0').append(new String(snr)).append(10 - sum % 10).toString(); } /** * 校验IMEI号码是否合法 * * @param imei 要校验的IMEI号码 * @return true表示IMEI号码合法,false表示IMEI号码不合法 */ public static boolean validateIMEI(CharSequence imei) { if (imei == null || imei.length() != 15) return false; return luhn(imei, 14) == imei.charAt(14) - '0'; } /** * IMEI号码的最后一位是校验位(Check Digit),它是根据IMEI号码前14位数字计算得出的一位数字。 * 校验位的作用是用于验证IMEI号码的正确性,防止因输入错误或篡改IMEI号码而导致的非法行为。 * <p> * Prior to 2002, the TAC was six digits and followed by a two-digit Final Assembly Code (FAC), which was a manufacturer-specific code indicating the location of the device's construction. From January 1, 2003 until April 1, 2004, the FAC for all phones was 00. After April 1, 2004, the Final Assembly Code ceased to exist and the Type Allocation Code increased to eight digits in length. * <p> * In any of the above cases, the first two digits of the TAC are the Reporting Body Identifier, which identifies the GSMA-approved group that allocated the TAC. The RBI numbers are allocated by the Global Decimal Administrator. IMEI numbers being decimal helps distinguish them from an MEID, which is hexadecimal and always has 0xA0 or larger as the first two hexadecimal digits. * <p> * For example, the old style IMEI code 35-209900-176148-1 or IMEISV code 35-209900-176148-23 tells us the following: * <p> * TAC: 35-2099 - issued by the BABT (code 35) with the allocation number 2099 * FAC: 00 - indicating the phone was made during the transition period when FACs were being removed. * SNR: 176148 - uniquely identifying a unit of this model * CD: 1 so it is a GSM Phase 2 or higher * SVN: 23 - The "software version number" identifying the revision of the software installed on the phone. 99 is reserved. * <p> * By contrast, the new style IMEI code 49-015420-323751-8 has an 8-digit TAC of 49-015420. * <p> * The new CDMA Mobile Equipment Identifier (MEID) uses the same basic format as the IMEI. * <p> * IMEI 的最后一个数字是一个校验位,使用 Luhn 算法计算,如 IMEI 分配和批准指南中所定义: * <p> * 校验位应根据Luhn公式(ISO/IEC 7812)计算。(见GSM 02.16 / 3GPP 22.016)。校验位是IMEI中所有其他数字的函数。手机的软件版本号 (SVN) 不包括在计算中。 * <p> * 校验位的目的是帮助防止 CEIR 和 EIR 设备输入不正确的可能性。 * <p> * 在标签和包装上以电子和印刷形式显示校验位非常重要。物流(使用条形码阅读器)和 EIR/CEIR 管理部门不能使用校验位,除非它打印在包装外和 ME IMEI/型号认证标签上。 * <p> * 校验位不会通过无线电接口传输,也不会在任何时候存储在 EIR 数据库中。因此,对IMEI最后三位或六位数字的所有引用都是指实际的IMEI号码,校验位不属于该号码。 * <p> * 校验位通过三个步骤进行验证: * <p> * 从右边开始,每隔一个数字加倍(例如,7 → 14)。 * 对数字求和(例如,14 → 1 + 4)。 * 检查总和是否可以被 10 整除。 * 相反,可以通过选择校验位来计算 IMEI,该校验位将给出可被 10 整除的总和。例如 IMEI 49015420323751? * |---------|---|------------|---|----|---|---|---|---|---|---|---|--------|---|---|------------| * | IMEI | 4 | 9 | 0 | 1 | 5 | 4 | 2 | 0 | 3 | 2 | 3 | 7 | 5 | 1 | x | * | 每隔一倍 | 4 | 18 | 0 | 2 | 5 | 8 | 2 | 0 | 3 | 4 | 3 | 14 | 5 | 2 | x | * | 总和数字 | 4 + (1 + 8) + 0 + 2 + 5 + 8 + 2 + 0 + 3 + 4 + 3 + (1 + 4) + 5 + 2 + x = 52 + x | * |--------------------------------------------------------------------------------------------| * 为了使总和能被 10 整除,我们设置 x = 8,因此完整的 IMEI 变为490154203237518 * * @param payload 验证的号码,不包含校验位的有效负载 * @param length 验证的号码的长度,有效负载的长度 * @return Check Digit 校验位 */ public static int luhn(CharSequence payload, int length) { int[] digits = new int[length]; for (int i = 0; i < length; i++) { // 在 ASCII 码表中,字符 '0' 的十进制码值为 48 // 使用 imei.charAt(i) - '0' 的方式获取每个字符代表的数字 digits[i] = payload.charAt(i) - '0'; } int sum = 0; for (int i = length - 1; i >= 0; i -= 2) if ((digits[i] *= 2) > 9) digits[i] = digits[i] / 10 + digits[i] % 10; for (int i = 0; i < length; i++) sum += digits[i]; return 10 - sum % 10; } }
梅森随机数旋转算法
用于生成梅森随机数
/** * 算法中用到的变量如下所示: * ·w:长度 生成的随机数的二进制长度 * ·n:寄存器长度 参与旋转的随机数个数(旋转的深度) * ·m:周期参数,用作第三阶段的偏移量 旋转算法参与旋转的中间项 * ·r:低位掩码/低位要提取的位数 内存界限值 2 的 r 次方 - 1 x⃗ (u)kx→k(u) 和 x⃗ (l)k+1x→k+1(l) 的切分位置 * ·a:旋转矩阵的参数 旋转算法异或基数 矩阵 AA 的最后一行 * ·f:初始化梅森旋转链所需参数 旋转链异或值膨化量 * ·u,s,t,l: 整数参数,移位运算的移动距离 * ·d,b,c: 比特遮罩 * ·s,t:TGFSR的位移量 * ·b,c:TGFSR的掩码 * ·u,d,l:额外梅森旋转所需的掩码和位移量 * <p> */ public class MT { private final short w, n, m, r; private final long a, f; private final byte u, s, t; private final long d, b, c; private final byte l; public enum Args { MT19937_32((short) 32, (short) 624, (short) 397, (short) 31, 0x9908B0DF, 1812433253, (byte) 11, 0xFFFFFFFF, (byte) 7, 0x9D2C5680, (byte) 15, 0xEFC60000, (byte) 18), MT19937_64((short) 64, (short) 312, (short) 156, (short) 31, 0xb5026f5aa96619e9L, 6364136223846793005L, (byte) 29, 0x5555555555555555L, (byte) 17, 0x71d67fffeda60000L, (byte) 37, 0xfff7eee000000000L, (byte) 43); private final short w, n, m, r; private final long a, f; private final byte u, s, t; private final long d, b, c; private final byte l; Args(short w, short n, short m, short r, long a, long f, byte u, long d, byte s, long b, byte t, long c, byte l) { this.w = w; this.n = n; this.m = m; this.r = r; this.a = a; this.f = f; this.u = u; this.d = d; this.s = s; this.b = b; this.t = t; this.c = c; this.l = l; } } private int index; private final long[] MT; // 312 * 64 - 31 = 19937 // 默认使用时间戳作为种子,使用MT19937-64位算法 public MT() { // this(System.currentTimeMillis()); } // 自定义随机种子 public MT(long seed) { this(seed, Args.MT19937_64); } public MT(Args var) { this(System.currentTimeMillis(), var); } public MT(long seed, Args var) { this(seed, var.w, var.n, var.m, var.r, var.a, var.f, var.u, var.d, var.s, var.b, var.t, var.c, var.l); } // 使用时间戳作为种子,自定义随机参数 public MT(short w, short n, short m, short r, long a, long f, byte u, long d, byte s, long b, byte t, long c, byte l) { this(System.currentTimeMillis(), w, n, m, r, a, f, u, d, s, b, t, c, l); } // 自定义随机参数和随机种子 public MT(long seed, short w, short n, short m, short r, long a, long f, byte u, long d, byte s, long b, byte t, long c, byte l) { this.w = w; this.n = n; this.m = m; this.r = r; this.a = a; this.f = f; this.u = u; this.d = d; this.s = s; this.b = b; this.t = t; this.c = c; this.l = l; MT = new long[this.n]; srand(seed); } // 由seed初始化获得基础的梅森旋转链 private void srand(long seed) { this.index = 0; this.MT[0] = seed; // 对数组其他元素进行初始化 for (int i = 1; i < this.n; i++) { this.MT[i] = (this.f * (this.MT[i - 1] ^ (this.MT[i - 1] >> (this.w - 2))) + i) & (long) (Math.pow(2, this.w) - 1); } } // 旋转算法处理旋转链 private void generate() { for (int i = 0; i < this.n; i++) { long lower_mask = -(1L << this.r); long upper_mask = ~lower_mask; long y = (this.MT[i] & upper_mask) + (this.MT[(i + 1) % this.n] & lower_mask); long yA = (y >> 1); if ((y % 2) != 0) { yA ^= this.a; } this.MT[i] = this.MT[(i + this.m) % this.n] ^ yA; } } // 对于旋转算法所得的结果进行处理 public synchronized long random() { if (this.index == 0) generate(); long y = this.MT[this.index]; y = y ^ ((y >> this.u) & this.d); y = y ^ ((y << this.s) & this.b); y = y ^ ((y << this.t) & this.c); y = y ^ (y >> this.l); this.index = (this.index + 1) % this.n; return y; } }
Object工具类
用来处理基本对象的一些附加操作
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.Objects; /** * @author Tenfond * @since 2023-05-04 */ public class ObjectUtil { private static final ObjectMapper JSON = new ObjectMapper(); public synchronized static String stringify(Object value) throws JsonProcessingException { return JSON.writeValueAsString(value); } public synchronized static <T> T parse(String content, Class<T> clazz) throws JsonProcessingException { return (T) JSON.readValue(content, clazz); } /** * 判断是否为null或默认值。 * 通过判断对象是否为空、布尔类型是否为 false、数字类型是否等于 0、字符串长度是否为 0、集合类型是否为空、数组长度是否为 0、字符类型是否等于 0 来判断对象是否为空或默认值。 * * @param value Object * @return 为空返回 true,否侧返回 false */ public static boolean isNone(Object value) { if (value == null) return true; // Boolean <-- boolean if (value instanceof Boolean) return !(Boolean) value; // Number <-- byte、short、int、long、float、double... if (value instanceof Number) return Objects.equals(value, 0); // CharSequence <-- String、StringBuilder、StringBuffer... if (value instanceof CharSequence) return ((CharSequence) value).length() == 0; // Collection <-- List、Set、Map、Queue... if (value instanceof Collection) return ((Collection<?>) value).isEmpty(); // Object[] <-- 任意类型数组的实例 if (value.getClass().isArray()) return Array.getLength(value) == 0; if (value instanceof Character) return (Character) value == 0; return false; } /** * 判断对象及其属性是否为null或默认构造对象。不支持接口、抽象类,因为无法获取他们的默认值,建议使用isNone()方法或equals()方法。 * 在 isNone 方法判断为 false 的情况下,通过反射获取对象的 Class 对象,调用其默认构造方法创建一个新的对象,然后再将原始对象和新对象进行比较,如果相等则说明对象及其属性为空或默认值。 * * @param value Object * @return 为null或默认值时返回 true,否则返回 false * @throws UnsupportedOperationException 不支持接口、抽象类,因为无法获取他们的默认值,建议使用isNone方法或equals方法 */ public static boolean isEntityNone(Object value) { if (isNone(value)) return true; try { Constructor<?> constructor = value.getClass().getDeclaredConstructor(); constructor.setAccessible(true); return equals(value, constructor.newInstance()); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) { // NoSuchMethodException 没有找到构造方法,代表该对象没有默认构造对象 // InstantiationException 没有无参构造,代表该对象没有默认构造对象 // IllegalAccessException 由接口、抽象类直接new出来的对象没有构造方法,代表该对象没有默认构造对象 return false; } catch (InvocationTargetException e) { // InvocationTargetException 构造函数抛出了一个异常;在构造函数中访问了一个 null 引用;构造函数中执行的方法抛出了一个异常;无法从构造函数中访问某些字段或方法 throw new UnsupportedOperationException(e); } } /** * 基于反射,判断两个对象及其属性是否完全相等。 * 递归遍历两个对象所有属性,比较是否相等,如果存在不相等的情况,则返回 false。 * * @param obj1 第一个对象 * @param obj2 第二个对象 * @return 如果相等返回 true,否则返回 false */ public static boolean equals(Object obj1, Object obj2) { if (obj1 == obj2) return true; if (obj1 == null || obj2 == null || obj1.getClass() != obj2.getClass()) return false; Class<?> clazz = obj1.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object value1; Object value2; try { value1 = field.get(obj1); value2 = field.get(obj2); } catch (IllegalAccessException e) { throw new UnsupportedOperationException(e); } if (field.getType().isPrimitive() && !Objects.equals(value1, value2)) { return false; } else if (!field.getType().isPrimitive() && !equals(value1, value2)) { return false; } } return true; } /** * @param value Boolean * @return 为空返回 true,否侧返回 false */ public static boolean isNone(Boolean value) { return value; } /** * @param value byte、short、int、long、float、double、char... * @return 为空返回 true,否侧返回 false */ public static boolean isNone(Number value) { return value == null || Objects.equals(value, 0); } /** * 判断是否为null或默认值 * * @param value String、StringBuilder、StringBuffer... * @return 为空返回 true,否侧返回 false */ public static boolean isNone(CharSequence value) { return value == null || value.length() == 0; } /** * 判断是否为null或默认值 * * @param value List、Set、Map、Queue... * @return 为空返回 true,否侧返回 false */ public static boolean isNone(Collection<?> value) { return value == null || value.isEmpty(); } /** * 判断是否为null或默认值 * * @param value 任意类型的数组 * @return 为空返回 true,否侧返回 false */ public static boolean isNone(Object[] value) { return value == null || value.length == 0; } /** * 将数字的每一位转换成一个byte,返回bytes数组 * * @param num 需要进行转换的数字 * @return 转换后得到的bytes数组 */ public static byte[] numberToArray(Number num) { String str = String.valueOf(num); byte[] array = new byte[str.length()]; for (int i = 0; i < str.length(); i++) { array[i] = (byte) (str.charAt(i) - '0'); } return array; } /** * 将数字的每一位转换成一个char,返回chars数组 * * @param num 需要进行转换的数字 * @return 转换后得到的chars数组 */ public static char[] numberToChars(Number num) { String str = String.valueOf(num); char[] chars = new char[str.length()]; for (int i = 0; i < str.length(); i++) chars[i] = str.charAt(i); return chars; } }
-
使用下面代码来生成设备信息
DeviceInfo deviceInfo = DeviceFactory.generateDeviceInfo(IMEI.Xiaomi_Redmi_K40); // IMEI.China 也可以 System.out.println(deviceInfo);
控制台输出
{ "deviceInfoVersion": 2, "data": { "display": "OPR1.201031.945", "product": "Redmi_K40", "device" :"w9vk", "board": "pzdc00", "brand": "Xiaomi", "model": "M201211AC", "bootloader": "unknown", "fingerprint": "Xiaomi/w9vk/pzdc00:8/OPR1/Redmi_K40:user/release-keys", "bootId": "A7ECAB40-0468-47EE-Y8DC1-C4AAE680DC225", "procVersion": "Linux version 205.181.159-x86_64 (ahj7pl34@2.42.186.14)", "baseBand": "", "version": { "incremental": "5355207", "release": "8", "codename": "REL" }, "simInfo": "China Telecom", "osType": "android", "macAddress": "5a:72:8c:77:51:6a", "wifiBSSID": "ec:59:8f:4b:d7:76", "wifiSSID": "<unknown ssid>", "imsiMd5": "842af0e36e9de8e49f5ce8475976ea65", "imei": "863074050407973", "apn": "wifi" } }