Android开发中有时候因业务需要APP端产生一个唯一的标识符使服务器能识别Android设备或与终端设备相互认证,目前一般使用三种标识符分别为DeviceId、AndroidId、MAC地址(WiFi、BT)。
一、DeviceId
DeviceId是Android系统为开发者提供的用于标识手机设备的串号,也是各种方法中普适性较高的,可以说几乎所有的设备都可以返回这个串号,并且唯一性良好。
DeviceId获取方法:
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String DeviceId = tm.getDeviceId();
根据不同的手机设备返回IMEI(移动或联通),MEID(电信)或者ESN码(电信),但在使用的过程中有以下问题:
- 非手机设备:最开始搭载Android系统都手机设备,而现在也出现了非手机设备:如平板电脑、电子书、电视、音乐播放器等。这些设备没有通话的硬件功能,系统中也就没有TELEPHONY_SERVICE,自然也就无法通过上面的方法获得DeviceId。
- 权限问题:获取DeviceId需要READ_PHONE_STATE权限,如果只是为了获取DeviceId而没有用到其他的通话功能,申请这个权限一来大才小用,二来部分用户会怀疑软件的安全性。
- 厂商定制系统中的Bug:少数手机设备上,由于该实现有漏洞,会返回垃圾,如:zeros或者asterisks
二、AndroidId(ANDROID_ID)
AndroidId是不需要权限的但是可能变的,在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是AndroidId,因是在用户第一次激活这个设备时产生的,所以当用户重置手机时,AndroidId会产生变化;理论上这个AndroidId是可以接受的,毕竟重置手机这个事发生也不会太频繁。可以通过下面的方法获取:
import android.provider.Settings;
String AndroidId = Settings.System.getString(getContentResolver(),Settings.System.ANDROID_ID);
AndroidId可以作为设备标识,但需要注意:
- 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。
- 厂商定制系统的Bug:有些设备返回的值为null。
- 设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
三、MAC地址(WiFi、BT)
我们也可以通过手机的Wifi或者蓝牙设备获取MAC ADDRESS作为DEVICE ID,但是并不推荐这么做:
- 硬件限制:并不是所有的设备都有Wifi和蓝牙硬件,硬件不存在自然也就得不到这一信息。
- 获取的限制:如果Wifi没有打开过,是无法获取其Mac地址的;而蓝牙是只有在打开的时候才能获取到其Mac地址。
在6.0 以下后去mac地址方式
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo winfo = wifi.getConnectionInfo();
String mac = winfo.getMacAddress();
在6.0 及其以上8.0以下,上面的方法在7.0 的设备获取的永远是 02:00:00:00:00:00
private static String getNewMac() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return null;
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:", b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
四、设备序列号
这个东西理论上来说是来自硬件,出厂是就设置好了,但是有些设备厂商会随便写一个值 Build.SERIAL 在 api>=26 的时候,可以这么获取:
Build.getSerial();
总结:上文可以看出,Android系统中并没有标准的获取所有厂商设备唯一ID的方法,各个方法都有自己的使用范围和局限性,这也是目前流行的Android系统版本过多,设备也是来自不同厂商,且没有统一标准等原因造成的。