标识一台 Android 设备已经是开发中司空见惯的事情了,但不是每个人都对自己所用 API 获取的标识有着明确的认知。所以,这里又将 Android Developers Blog 2011年3月30日的一篇文章《Identifying App Installations》 翻了出来,这篇文章里的观点也大致的体现了 Google 官方对标识 Android 设备的态度:**建议使用 UUID 作为标识符。**博客里甚至还贴出了示例代码,建议应用在安装后第一次运行时创建一个新的 UUID 标识。以下是示例代码:
1 | public class Installation { |
看完这段代码,很多人会固执的认为这依然不是自己想要的解决方案。毕竟,这个标识会随着用户的卸载和重装发生变化,因为每次生成的 UUID 都会不一样,也就对持久的追踪和标识这台设备产生了各种不确定性。我们更想要的是一个硬件设备的标识符,而这个标识符又能像每个人的身份证号码一样持久可靠。我们能想到的难道 Android 官方想不到吗?来看看官方的解释:
In the Android group, from time to time we hear complaints from developers about problems they’re having coming up with reliable, stable, unique device identifiers. This worries us, because we think that tracking such identifiers isn’t a good idea, and that there are better ways to achieve developers’ goals.
所以,Android 官方提供了文章开头部分的解决方案:使用 UUID 作为标识符。这也间接的说明,官方从主观上是拒绝对一台设备进行持久的追踪的。毕竟,每个国家对隐私的认知标准不一样。当然,如果开发者坚持要使用设备的硬件标识符来进行追踪,Android 官方也列出了几种可以作为标识符的选项:
设备标识
在以往,每台 Android 设备都是手机的时候,我们可以通过 TelephonyManager.getDeviceId()
获取到手机硬件特有的 IMEI,MEID 或 ESN。但是,这种方法存在问题:
- 非电话:没有电话模块的平板或音乐播放器就没有这种唯一标识。
- 持久性:在具有此功能的设备上,它会在设备数据擦除和工厂重置中持续存在。现在还不清楚,在这种情况下,你的应用是否应该将它视为相同的设备。
- 权限:需要权限 android.permission.READ_PHONE_STATE。
- 错误:部分手机实例的实现是错误的,会返回垃圾信息,例如零或星号。
MAC地址
可以从设备的 WiFi 或蓝牙硬件中检索 Mac 地址。我们不建议将其用作唯一标识符。首先,并非所有设备都有 WiFi。此外,如果未打开 WiFi,则硬件可能不会报告 Mac 地址。
序列号
从 Android 2.3(Gingerbread)开始,这可以通过 android.os.Build.SERIAL
获得。没有电话的设备需要在此处报告唯一的设备 ID;有些手机也可以这样做。从 Android 9(API level 28)开始,为了保护用户隐私,Build.SERIAL
会返回 UNKNOWN
。如果你的应用需要获取硬件序列号,你应该先申请 READ_PHONE_STATE
权限,然后调用 getSerial()
方法。
ANDROID_ID
更具体地说是 Settings.Secure.ANDROID_ID
。这是在设备首次启动时生成并存储的 64 位数。擦除设备时会重置。
ANDROID_ID
似乎是唯一设备标识符的不错选择。但有一些缺点:首先,它在 2.2 之前的 Android 版本(Froyo)上并非 100% 可靠。此外,在主要制造商的流行手机中至少有一个经常被发现的错误,就是每个实例都具有相同的 ANDROID_ID
。
结论
对于绝大多数应用程序,要求是识别特定安装,而不是物理设备。幸运的是,这样做很简单。
有很多充分的理由可以避免识别特定的设备。但对于那些想要尝试的人来说,最好的方法可能是在任何合理的现代化设备上使用 ANDROID_ID
,并为传统设备提供一些后备启发式算法。如果还想更深入的了解设备的识别,请参阅《唯一标识符最佳做法》。