Android设备唯一标识

背景

Android系统中并没有可靠获取所有厂商设备唯一ID的方法,各个方法都有自己的使用范围和局限性,这也是目前流行的Android系统版本过多,设备也是来自不同厂商,且没有统一标准等原因造成的。

常规方案

  • DEVICE_ID
  • MAC ADDRESS
  • 设备序列号Serial Number
  • ANDROID_ID
  • 手机硬件信息
DEVICE_ID

DEVICE_ID可以同通过getSystemService(Context.TELEPHONY_SERVICE).getDeviceId()获取,它根据不同的手机设备返回IMEI,MEID或者ESN码

缺点

非手机设备无法获取
权限问题(6.0以上需要申请权限)

转发请指明出处:

MAC ADDRESS

获取方式:

WifiManager wifiManager=(WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo=wifiManager.getConnectionInfo();
String mac=wifiInfo.getMacAddress();

缺点

权限问题(6.0以上需要申请权限)

另外一种获取方式:

NetworkInterface networkInterface = NetworkInterface.getByName("wlan0");
byte[] mac = networkInterface.getHardwareAddress();

缺点

  • 如果重启手机后,Wifi没有打开过,是无法获取其Mac地址的
  • mac变更问题
  • 需要 ACCESS_WIFI_STATE 权限
设备序列号Serial Number

获取方式:

String serialNum = android.os.Build.SERIAL;
//装有SIM卡的设备获取办法:getSystemService(Context.TELEPHONY_SERVIEC).getSimSerialNumber();

缺点

  • CDMA设备,返回的是一个空值。
  • 少数的一些设备上,会返回垃圾数据。
  • 对于没有通话功能的设备,它可能会返回一个固定的值
ANDROID_ID

获取方式:

import android.provider.Settings;   
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);

缺点

  • 当设备被恢复出厂设置后该值会被重置
  • 在Android <=2.1 or Android >=2.3的版本是可靠、稳定的,但在2.2的版本并不是100%可靠的
  • 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID
  • 厂商定制系统的Bug:有些设备返回的值为null

手机硬件信息(推荐)

android.os.Build类中,包括了这样的一些信息(如:手机制作商,系统版本等)。我们可以直接调用 而不需要添加任何的权限和方法。

即使重置系统一样唯一

android.os.Build.BOARD:获取设备基板名称
android.os.Build.BOOTLOADER:获取设备引导程序版本号
android.os.Build.BRAND:获取设备品牌
android.os.Build.CPU_ABI:获取设备指令集名称(CPU的类型)
android.os.Build.CPU_ABI2:获取第二个指令集名称
android.os.Build.DEVICE:获取设备驱动名称
android.os.Build.DISPLAY:获取设备显示的版本包(在系统设置中显示为版本号)和ID一样
android.os.Build.FINGERPRINT:设备的唯一标识。由设备的多个信息拼接合成。
android.os.Build.HARDWARE:设备硬件名称,一般和基板名称一样(BOARD)
android.os.Build.HOST:设备主机地址
android.os.Build.ID:设备版本号。
android.os.Build.MODEL :获取手机的型号 设备名称。
android.os.Build.MANUFACTURER:获取设备制造商
android:os.Build.PRODUCT:整个产品的名称
android:os.Build.RADIO:无线电固件版本号,通常是不可用的 显示unknown
android.os.Build.TAGS:设备标签。如release-keys 或测试的 test-keys 
android.os.Build.TIME:时间
android.os.Build.TYPE:设备版本类型  主要为"user" 或"eng".
android.os.Build.USER:设备用户名 基本上都为android-build
android.os.Build.VERSION.RELEASE:获取系统版本字符串。如4.1.2 或2.2 或2.3等
android.os.Build.VERSION.CODENAME:设备当前的系统开发代号,一般使用REL代替
android.os.Build.VERSION.INCREMENTAL:系统源代码控制值,一个数字或者git hash值
android.os.Build.VERSION.SDK:系统的API级别 一般使用下面大的SDK_INT 来查看
android.os.Build.VERSION.SDK_INT:系统的API级别 数字表示

根据以上信息,定制自己的唯一值规则。如:

public static String getUUID() {
        String serial = null;
        String m_szDevIDShort = "随机两位数" +
                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 位
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                serial = android.os.Build.getSerial();
            } else {
                serial = Build.SERIAL;
            }
            //API>=9 使用serial号
            return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
        } catch (Exception exception) {
            //serial需要一个初始化
            serial = "默认值"; // 随便一个初始化
        }
        //使用硬件信息拼凑出来的15位号码
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    }

以上为自己的看法,如果有更好的解决方案,欢迎留言评论互相学习。