关于移动设备唯一标识的获取

起因

我们在做移动端开发时, 不可避免地会遇到需要获取设备唯一标识的问题

如果仅仅只需要给设备去重, 通常情况下我们可以考虑直接使用推送SDK生成的设备Token, 比如极光推送, 或者腾讯TPNS, App重装后这个token可能会变, 但重复的概率极低 只是并不具备唯一性

想要唯一性 以安卓设备为例, 其实有很多设备码可用, 比如IMEI, MAC地址等等, 但是为什么我们在实际应用中无法将它们用作唯一标识呢

我们挨个来分析一下

IMEI

Android中也叫DEVICE_ID, 是移动电话的唯一设备码, 相当于手机的身份证, 具备唯一性

但是:

  • 有通话功能的设备才有该码, 不是所有移动设备都具备拨号功能
  • 双卡双待会有两个IMEI
  • 模拟器可以修改IMEI
  • Android10以上无法获取IMEI

网卡 Mac地址

其中包含硬件MAC地址和WLAN MAC地址(BSSID), 硬件MAC地址和硬件绑定, 具备唯一性 , 但是WLAN MAC地址通常是随机的 主要是为了避免隐私泄露, 每个热点在连接时会使用随机的地址

可即便是硬件Mac也存在不足之处:

  • 有网卡的设备才有Mac地址, 不是所有设备都具备联网功能
  • Mac地址可以通过软件欺骗性修改

蓝牙Mac地址

WLAN Mac一样具备唯一性

但是:

  • 没有蓝牙的设备获取不到该值
  • 蓝牙需要保持开启状态才有值

ANDROID_ID

官方推荐使用的设备码, 具备唯一性

但是:

  • 设备恢复出厂设置 或者ROOT后 该值会改变
  • 国内定制系统的设备可能返回null
  • Android8以下无法获取Android_ID

PsuedoID

该方式是通过拼接硬件信息获取到的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//获得独一无二的Psuedo ID
public static String getUniquePsuedoID() {
String serial = null;

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 位

try {
serial = android.os.Build.class.getField("SERIAL").get(null).toString();
//API>=9 使用serial号
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
//serial需要一个初始化
serial = "serial"; // 随便一个初始化
}
//使用硬件信息拼凑出来的15位号码
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();

最终会得到类似于这样的一串ID:00000000-088ee-388eb-ffff-ffffe93ee2

这种方案只能做到小范围内唯一

因为:

  • 系统升级后 该值可能会变化
  • 同一批次出厂的设备可能出现值重复

总体来说 该方案可行性相对较高一些 重复的概率极低, 而且不需要获取用户权限

但是依然不够唯一

总结

以上所有的方案似乎都只是理论唯一, 真要用到实际产品中还是差了点, 如果非要准确一些可以考虑以下方案:

第一种
  • Android 8.0以下 使用 PsuedoID + DeviceId 来判断设备唯一性
  • Android 8.0及以上 使用PsuedoID + ANDROID_ID来判断设备唯一性
第二种

如果忽略大部分不能联网的设备, 可以使用Mac地址作为唯一标识, 由于Android6.0以上的版本无法获取Mac地址, 我们可以考虑从底层文件获取, Mac地址文件路径为:

1
/sys/class/net/wlan0/address

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun getMacAddr(): String {
return try {
loadFileAsString("/sys/class/net/wlan0/address")
.toUpperCase().substring(0, 17)
} catch (e: IOException) {
e.printStackTrace()
""
}
}

@Throws(IOException::class)
private fun loadFileAsString(filePath: String): String {
val fileData = StringBuffer(1000)
val reader = BufferedReader(FileReader(filePath))
val buf = CharArray(1024)
var numRead = 0
while (reader.read(buf).also { numRead = it } != -1) {
val readData = String(buf, 0, numRead)
fileData.append(readData)
}
reader.close()
return fileData.toString()
}
第三种

使用第三方设备唯一标识提供商, 比如数字联盟, 对于AndroidIOS设备均适用

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

0%