之前项目中的方案是
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;
}