一,Android常见的唯一标识
IMEI,MEID,ESN,IMSI,android_id 目前用的最多的就是IMEI 现在Android10 是真的获取不到IMEI标识了!现在移动安全联盟出的那个架包 目前只支持国内手机,三星手机不支持,而且 国内手机也不一定全部支持
二,生成UUID保存在本地,
在Android10 之前,可以创建一个文件保存一个编号,多个应用都能访问,但是没删除掉了 或者 刷机,或者恢复出厂设置了 都不能用了,现在Android 储存权限更一步提升,保存的文件 应用卸载 文件就删除了!除非用mediaStore 保存多媒体文件,但是多个应用不能共享使用,这个问题非常头疼
三,解决方案
1,本地生成一个UUID当唯一标识
2,获取硬件的所有硬件编码和UUID进行绑定,上传数据库,如果应用本删除或者卸载,下次进入应用根据硬件编码 获取UUID,下面直接贴代码
一,生成UUID
//生成15位唯一性的订单号
private static String getUUID() {
//随机生成一位整数
int random = (int) (Math.random() * 9 + 1);
String valueOf = String.valueOf(random);
//生成uuid的hashCode值
int hashCode = UUID.randomUUID().toString().hashCode();
//可能为负数
if (hashCode < 0) {
hashCode = -hashCode;
}
String value = valueOf + String.format("%014d", hashCode);
return value;
}
二,获取目前能获取的唯一编号和硬件编码
public static String getDeviceInformJson() {
DeviceInformModel deviceInformModel = new DeviceInformModel();
deviceInformModel.setMac(DeviceId.getMacAddress());//获取mac
deviceInformModel.setAndroid_id(DeviceId.getAndroidID(App.getsInstance()));//获取AndroidId
deviceInformModel.setSerial_no(DeviceId.getSerialNo());//获取Sim卡编码
deviceInformModel.setImsi(DeviceId.getSubscriberId());//获取Imsi编码
deviceInformModel.setSimserial(DeviceId.getSimSerialNumber());
deviceInformModel.setFingerprint(DeviceId.getFingerprint());//获取指纹编码
deviceInformModel.setSplitNo(DeviceId.getSplitNo());//多个字段拼接的编码
deviceInformModel.setPhysics_info(HexUtil.long2Hex(PhysicsInfo.getHash(App.getsInstance())));//获取硬件编码信息
deviceInformModel.setDark_physics_info(HexUtil.long2Hex(DarkPhysicsInfo.getHash(App.getsInstance())));//获取物理编码Hash值
return GsonUtil.getInstance().GsonString(deviceInformModel);
}
上面代码是获取硬件编码并转化成json字符串 和uuId一块传给后台的
三 ,获取硬件编码的代码
package com.xhs.baselibrary.utils.device;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.core.app.ActivityCompat;
import com.xhs.baselibrary.App;
import com.xhs.baselibrary.utils.Encryption;
import java.lang.reflect.Method;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.UUID;
public class DeviceId {
private static final byte[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private static final String INVALID_MAC_ADDRESS = "02:00:00:00:00:00";
private static final String INVALID_ANDROID_ID = "9774d56d682e549c";
private static final String INVALID_Serial_No = "unknown";
private static byte[] getMacInArray() {
try {
Enumeration enumeration = NetworkInterface.getNetworkInterfaces();
if (enumeration == null) {
return null;
}
while (enumeration.hasMoreElements()) {
NetworkInterface netInterface = enumeration.nextElement();
if (netInterface.getName().equals("wlan0")) {
return netInterface.getHardwareAddress();
}
}
} catch (Exception e) {
Log.e("tag", e.getMessage(), e);
}
return null;
}
public static long getLongMac() {
byte[] bytes = getMacInArray();
if (bytes == null || bytes.length != 6) {
return 0L;
}
long mac = 0L;
for (int i = 0; i < 6; i++) {
mac |= bytes[i] & 0xFF;
if (i != 5) {
mac <<= 8;
}
}
return mac;
}
public static String getMacAddress() {
String mac = formatMac(getMacInArray());
if (TextUtils.isEmpty(mac) || mac.equals(INVALID_MAC_ADDRESS)) {
return "";
}
return mac;
}
private static String formatMac(byte[] bytes) {
if (bytes == null || bytes.length != 6) {
return "";
}
byte[] mac = new byte[17];
int p = 0;
for (int i = 0; i <= 5; i++) {
byte b = bytes[i];
mac[p] = HEX_DIGITS[(b & 0xF0) >> 4];
mac[p + 1] = HEX_DIGITS[b & 0xF];
if (i != 5) {
mac[p + 2] = ':';
p += 3;
}
}
return new String(mac);
}
public static String getAndroidID(Context context) {
if (context != null) {
@SuppressLint("HardwareIds")
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
if (!TextUtils.isEmpty(androidId) && !INVALID_ANDROID_ID.equals(androidId)) {
return androidId;
}
}
return "";
}
/**
* 获取手机序列号
*
* @return 手机序列号
*/
public static String getSerialNo() {
String serial = "";
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+
if (ActivityCompat.checkSelfPermission(App.getsInstance(), Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
serial = Build.getSerial();
}
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
serial = Build.SERIAL;
} else {//8.0-
Class> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("e", "读取设备序列号异常:" + e.toString());
}
if (!TextUtils.isEmpty(serial) && !INVALID_Serial_No.equals(serial)) {
return serial;
}
return "";
}
public static String getSubscriberId() {
TelephonyManager mTelephonyMgr = (TelephonyManager) App.getsInstance().getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(App.getsInstance(), Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
String imsi = mTelephonyMgr.getSubscriberId();
if (!TextUtils.isEmpty(imsi)) {
return imsi;
}
return imsi;
}
return "";
}
public static String getSimSerialNumber() {
TelephonyManager mTelephonyMgr = (TelephonyManager) App.getsInstance().getSystemService(Context.TELEPHONY_SERVICE);
//返回SIM卡的序列号
if (ActivityCompat.checkSelfPermission(App.getsInstance(), Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
String simSerialNumber = mTelephonyMgr.getSimSerialNumber();
if (!TextUtils.isEmpty(simSerialNumber)) {
return simSerialNumber;
}
}
return "";
}
public static String getSplitNo() {
String serial = "";
String m_szDevIDShort = "35" +
Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +
Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +
Build.USER.length() % 10; //13 位
serial = getSerialNo();
//使用硬件信息拼凑出来的15位号码
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
public static String getFingerprint() {
return Encryption.encrypByMD5(android.os.Build.FINGERPRINT);
}
}
四, 后台逻辑
1,App 请求参数是本地uuId或者刚生成的UUId 和所有App硬件编码标识
2,后台先根据uuId查询是否存在这个编码,如果存在就返回 这条信息,如果没有 根据硬件编码标识 如果不为空,而且硬件编码标识和数据库里面 有两条一样的 那么把这条记录返回给客户端,那么返回的uuId 就是 之前保存的那个uuId
代码还有很多如果需要 我贴github上面,如果不明白其中逻辑的,可以评论咨询,时间忙 记录一下这个方案