前言
公司开发的游戏需要对用户设备进行标识,获得一个稳定可靠并且唯一的识别码,来作游客登录。并考虑后续玩家从游客换成账户登录数据要同步过来。
虽然Android系统中提供了这样设备识别码,但是由于Android系统版本、厂商定制系统中的Bug等限制,稳定性和唯一性并不理想。而通过其他硬件信息标识也因为系统版本、手机硬件等限制存在不同程度的问题。
下面汇总了一些能作为设备唯一标识的方案。
方案
方案大致分为三个方向,第一个方向就是安卓系统提供的一系列接口,获取具有唯一属性的码,比如imei,MAC,android_id等;第二个方向就是unity自带的API;第三个方向就是自定义计算ID。
一.安卓系统原生
如果不会unity与安卓交互可参考我的上一篇博客:Android与Unity交互及手机震动控制
安卓系统获取串码
参考资料: 获取Android设备唯一标识码
参考资料: 获取Android设备的唯一标识符
参考资料: Android设备唯一标识
参考资料: Android 手机获取Mac地址的方法
主要串码:
IMEI:(国际移动设备识别码(IMEI:International Mobile Equipment Identification Number)是区别移动设备的标志,储存在移动设备中,可用于监控被窃或无效的移动设备。
android系统中通常用下面这段代码获取:
/**
* 获取手机IMEI号
*
* 需要动态权限: android.permission.READ_PHONE_STATE
*/
public static String getIMEI(Context context) {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(context.TELEPHONY_SERVICE);
String imei = telephonyManager.getDeviceId();
return imei;
}
MEID:Mobile Equipment IDentifier(MEID)是全球唯一的56bit移动终端标识号。标识号会被烧入终端里,并且不能被修改。
没有统一的获取方法!
MAC ADDRESS:(Media Access Control Address),直译为媒体存取控制位址,也称为局域网地址(LAN Address),MAC位址,以太网地址(Ethernet Address)或物理地址(Physical Address),它是一个用来确认网络设备位置的位址。
android系统中通常用下面这段代码获取:
/**
* 通过WiFiManager获取mac地址
* @param context
* @return
*/
private static String tryGetWifiMac(Context context) {
WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wi = wm.getConnectionInfo();
if (wi == null || wi.getMacAddress() == null) {
return null;
}
if ("02:00:00:00:00:00".equals(wi.getMacAddress().trim())) {
return null;
} else {
return wi.getMacAddress().trim();
}
}
ANDROID_ID:在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。
android系统中通常用下面这段代码获取:
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);
android.os.Build:根据手机硬件的型号返回串码,所以不同手机具有不同的串码,但是同型号的手机会重码。
android系统中通常用下面这段代码获取:
设备序列号(Serial Number, SN)
String serialNum = android.os.Build.SERIAL;
制造商 (Manufacturer)
String manufacturer = android.os.Build.MANUFACTURER;
型号(Model)
String model = android.os.Build.MODEL;
品牌(Brand)
String brand = android.os.Build.BRAND;
设备名 (Device)
String device = android.os.Build.DEVICE;
二.Unity自带API
参考资料官方API文档
使用Unity自带的API,SystemInfo.deviceUniqueIdentifier获取。
string 设备唯一标识符= SystemInfo.deviceUniqueIdentifier;
三.自定义计算ID
这一套方案就是,服务器端采用流水号算法生成唯一码,然后下推给客户端,客户端将这个唯一码保存,最好设置为初始时自动截屏把唯一码保存在相册里。
方案对比
踩坑与解决
-
天坑1:在采用安卓系统原生的方案时,起初使用的是imei码,装上手机发现重号。后来发现这个码在安卓P之后压根不让你获取,会返回空值,所以重号。
-
天坑2:后来加上设备硬件计算串码,刚开始还好,没什么问题,后来两台同型号的手机鬼使神差般的重号了,原因是当手机型号相同,通过手机硬件获取的串码就会一样,那么重号就不奇怪了。
-
天坑3:在采用了 imei和硬件设备串码后,还得继续添加新的串码段,经研究android_id的串码是imei后最为合适的,然后就把android_id加上了,这下很欣喜发现不重号了,同型号手机也不同号,好了可以放心了。好景不长,原先打包的同学没在不能打包了,于是用自己的电脑打包,这下好,一安装,全体的ID都成新的了,之前升级了的数据都不在了。研究了好久,发现自Android 8.0(API级别26)起,ANDROID_ID取决于应用程序的签名密钥。这意味着“未签名”构建(默认情况下使用调试密钥库进行签名)的值将不同于已签名构建(使用播放器设置中提供的密钥进行签名)。所以也就有了换一台机器打包就有不同的串码值。
-
解决:踩了这么多坑怎么解决呢?如上面所说,想要达到不同的机器打包出的串码都是一致的,需要做一个密钥,然后大家的电脑都使用这个密钥,然后结合 ANDROID_ID返回的串码使用。这样 获取手机设备唯一标识算告一段落了。但仍不是完美的方案。
-
不完美 :为什么上述的不是完美的方案呢?因为各手机厂商不同ANDROID_ID的计算方式有所不同,会可能出现重号的现象,概率很小。除此之外由于Android系统版本、厂商定制系统中的Bug等限制,稳定性和唯一性并不理想。使用的体量一大,说不定也会出现重号和换号的情况。那有没有更好的方案,我想没有,但有可选方案,就是我上面说的自定义ID,这个方案的好处是,可以保证ID的唯一性,缺点就是玩家一旦卸载,再重新安装就会视为新用户,如果想找回账户,得查看手机里自动保存到相册的账号图片,经过一番找回操作才行。万一玩家将这张图片删除或清除了,那想要找回就只能是天方夜谭。
总结
获取设备唯一标识符没有完美的方案,做联网手游应当拒绝这个需求,直接引导玩家进行账号注册。
当然在临时测试阶段使用安卓系统原生的方式,是一个不错的选择。
文中的代码及工程下载:Android2Unity.zip