之前项目中的方案是Settings.System.getString(context.getContentResolver(), "YOUR_KEY");
先从系统设置中获取,若获取不到,本地按照时间戳和随机数生成一个deviceId写入到系统设置中Settings.System.putString(context.getContentResolver(), "YOUR_KEY", deviceId);
以后获取时就可以从系统设置中获取,app卸载后重新安装仍可以获取到此字段。
但是,经测试在公司一部vivo(5.0)手机上是正常的(卸载后,可获取到自己保存的字段),在Red3s(6.0)上就获取不到了即每次重新安装app都是一个新的deviceId,debug看到也写入成功了,可能是6.0以后,应用卸载后会把相关系统设置擦除吧,而且,由于之前公司应用targetsdkversion是22,不需要处理权限,19年8月开始,targetsdkversion26以下的不能在主流应用商店更新,所以要兼容到26,就需要申请权限况且卸载后此字段又没有被保存,决定换种方案。
参考:
https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html
1. imei
国际移动设备识别码,是第一个想到的,是手机的身份证,刷机不会被改变
缺点:
- 只适用于电话功能设备 不适用于pad
- 6.0以后需要动态申请权限
- 有些设备会返回错误
2.手机WiFi或蓝牙的MAC地址
缺点:
- 硬件限制:并不是所有的设备都有WiFi和蓝牙硬件 但其实大部分设备都是支持wifi和蓝牙的了,所以这条并不重要
- 如果WiFi没有打开过,是无法获取其Mac地址的(高版本获取到的mac将是固定的:02:00:00:00:00:00);
高版本好像有方法可以获取到,但也要适配一系列版本,以后变动要持续适配 - 蓝牙是只有在打开的时候才能获取到其Mac地址(需要动态申请权限)
**
3.Serial Number
硬件序列,在Android 2.2 以上可以通过 android.os.Build.SERIAL 获得序列号
缺点:
https://stackoverflow.com/questions/11029294/android-how-to-programmatically-access-the-device-serial-number-shown-in-the-avhttps://developer.android.com/reference/android/os/Build.html#SERIAL
A hardware serial number, if available. Alphanumeric only, case-insensitive. For apps targeting SDK higher than Build.VERSION_CODES.O_MR1 this field is set to UNKNOWN.
硬件序列号(如果有)。 仅限字母数字,不区分大小写。 对于定位SDK高于Build.VERSION_CODES.O_MR1的应用,此字段设置为UNKNOWN。
26以后被弃用,getSerial ()替代,需要动态申请READ_PHONE_STATE权限
4.android Id
设备首次启动产生的,不需要获取权限
8.0中,只要程序包名称和签名密钥相同,ANDROID_ID值就不会在程序包卸载/重新安装时更改
获取方法:
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
缺点:
- 设备恢复出厂设置会被重置
- 部分手机返回为空或固定值9774d56d682e549c
考虑到动态申请权限成本较高,可接受恢复出厂重置androidId
公司几部测试机从4.4 5.0 6.0 7.0 8.0 均测试以下方法能成功获取到相同androidId
经上几种比较android id方式较为可靠
最终方法如下:
private static String deviceId = null;
public static String getDeviceId() {
if (!TextUtils.isEmpty(deviceId)) {
return deviceId;
}
if (!TextUtils.isEmpty(SPUtils.getStringValue(DEVICE_ID_KEY, ""))) {
//从sp中获取
deviceId = SPUtils.getStringValue(DEVICE_ID_KEY, "");
}else{
//sp中没有
String uuId;
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
if (TextUtils.isEmpty(androidId) || TextUtils.equals("9774d56d682e549c", androidId)) {
uuId = UUID.randomUUID().toString();
} else {
//构造UUID,防止直接暴露ANDROID_ID
uuId = new UUID(androidId.hashCode(), ((long) androidId.hashCode() << 32)).toString();
}
deviceId = uuId.replace("-", "");
//保存sp
SPUtils.setStringValue(DEVICE_ID_KEY, deviceId);
}
return deviceId;
}