稳定获取Android设备唯一码(UUID)的解决方案
最近做的一个项目中需要用到Android设备唯一码(UUID)来标识一台设备,
Android中设备唯一码有很多,如:MAC地址、IMEI号(DeviceId)、IMSI号、ANDROID_ID、序列号(SerialNumber)等,
但并不是所有设备上都能稳定获取到这些值。
最后项目中采用的是MAC地址。
先总结一些搜索得知的各种值的缺点,再说说最后采用MAC地址的解决方案吧。
1.MAC地址:
获取MAC地址的方法有两种:
(1). 通过Linux命令查询
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
 | 
public String getMacAddress() {     String macAddress = null ;     String str = "" ;     try {        //linux下查询网卡mac地址的命令         Process pp = Runtime.getRuntime().exec( "cat /sys/class/net/wlan0/address" );         InputStreamReader ir = new InputStreamReader(pp.getInputStream());         LineNumberReader input = new LineNumberReader(ir);         for (; null != str;) {             str = input.readLine();             if (str != null ) {                 macAddress = str.trim();// 去空格                 break ;             }         }     } catch (IOException ex) {         ex.printStackTrace();     }     return macAddress;} | 
缺点:
在当前没打开WiFi的情况下获取得到的MAC地址值为空,即使在执行这段代码前是有打开过WiFi,而执行这段代码时WiFi状态是关闭的,也不能获取到MAC地址。
(2). 通过Android官方的WifiManager类获取
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | 
public String getMacAddress() {     String macAddress = null ;              WifiManager wifiManager =         (WifiManager)MyApplication.getContext().getSystemService(Context.WIFI_SERVICE);     WifiInfo info = ( null == wifiManager ? null : wifiManager.getConnectionInfo());              macAddress = info.getMacAddress();     return macAddress;} | 
需要加入权限
| 
 1 
 | 
< uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" /> | 
缺点:
这种方法虽然能在当前Wifi状态为关闭的情况下获取到MAC地址,但前提是在手机开机后要打开过一次Wifi,如果在某次开机后没打开过Wifi就调用这段代码,获取地址也是为空。
网上给出的解释是:WiFi的Mac address是一个被动资讯。一般在开机后,不会主动上报到系统裡。要待WiFi硬件启动后,才会把有关Mac address资料记载入系统去。
2.IMEI号(DeviceId)、IMSI号:
| 
 1 
2 
3 
 | 
TelephonyManager mTelephonyMgr = (TelephonyManager) getSystemServic(Context.TELEPHONY_SERVICE);String imsi = mTelephonyMgr.getSubscriberId(); //获取IMSI号String imei = mTelephonyMgr.getDeviceId(); //获取IMEI号 | 
需要加入权限
| 
 1 
 | 
< uses-permission android:name = "android.permission.READ_PHONE_STATE" /> | 
缺点:
IMEI号(国际移动设备身份码)、IMSI号(国际移动设备识别码)这两个是有电话功能的移动设备才具有,也就是说某些没有电话功能的平板是获取不到IMEI和IMSI号的。且在某些设备上getDeviceId()会返回垃圾数据。
3.ANDROID_ID:
ANDROID_ID 是设备首次启动时由系统随机生成的一串64位的十六进制数字。
| 
 1 
 | 
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID); | 
缺点:
①.设备刷机wipe数据或恢复出厂设置时ANDROID_ID值会被重置。
②.现在网上已有修改设备ANDROID_ID值的APP应用。
③.某些厂商定制的系统可能会导致不同的设备产生相同的ANDROID_ID。
④.某些厂商定制的系统可能导致设备返回ANDROID_ID值为空。
⑤.CDMA设备,ANDROID_ID和DeviceId返回的值相同
4.序列号SerialNumber:
从Android 2.3开始,通过android.os.Build.SERIAL方法可获取到一个序列号。没有电话功能的设备也都需要上给出此唯一的序列号。
| 
 1 
 | 
String SerialNumber = android.os.Build.SERIAL; | 
缺点:
在某些设备上此方法会返回垃圾数据。
解决方案:
一种比较折衷的办法,在获取MAC地址之前先判断当前WiFi状态,若开启了Wifi,则直接获取MAC地址,若没开启Wifi,则用代码开启Wifi,然后马上关闭,再获取MAC地址。
目前此方法测试成功,无论在哪种状态下都能正确取得设备的MAC地址(包括开机后未启动过Wifi的状态下),且在未开启Wifi的状态下,用代码开启Wifi并马上关闭,过程极短,不会影响到用户操作。
代码如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
 | 
public String getMacAddress() {     String macAddress = null ;     WifiManager wifiManager =         (WifiManager)MyApplication.getContext().getSystemService(Context.WIFI_SERVICE);     WifiInfo info = ( null == wifiManager ? null : wifiManager.getConnectionInfo());              if (!wifiManager.isWifiEnabled())     {        //必须先打开,才能获取到MAC地址         wifiManager.setWifiEnabled( true );         wifiManager.setWifiEnabled( false );     }     if ( null != info) {         macAddress = info.getMacAddress();     }     return macAddress;} | 
需要加入如下权限
| 
 1 
2 
3 
 | 
< uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" > </ uses-permission >< uses-permission android:name = "android.permission.CHANGE_WIFI_STATE" > </ uses-permission >< uses-permission android:name = "android.permission.WAKE_LOCK" > </ uses-permission > | 
来自:http://www.goteny.com/develop/android/201412/452.html
极光推送的设备唯一性标识 RegistrationID
对于 App 云平台系统,如何唯一地识别移动设备是非常重要的。否则,每次用户在设备上卸载掉应用再重新安装,后端系统只能把这个用户当作一个全新的用户了。
Android 上识别设备的唯一性,因为这个圈太乱,所以设备本身的任何标识都是无法直接用作设备唯一标识的。iOS 是系统强力限制被唯一识别的,目前唯一可以部分满足条件是 IDFA,但需要你的 App 本身的确嵌入了广告。
很多开发者使用极光推送时,都有这个疑问:极光推送是如何来做设备的唯一性识别的。本文解析极光推送如何尽可能地来唯一识别设备。
极光推送对安装在设备上的 App 使用 RegistrationID 作为标识。极光推送要『尽可能』确保设备的唯一性,就是要使得 RegistrationID 尽可能唯一。
RegistrationID 的定义
关于 RegistrationID 极光官方文档有如下的定义:
集成了 JPush SDK 的应用程序在第一次 App 启动后,成功注册到 JPush 服务器时,JPush 服务器会给客户端返回唯一的该设备的标识 – RegistrationID。JPush SDK 会以广播的形式发送 RegistrationID 到应用程序。
有了这个标识,App 编程可以把这个 RegistrationID 保存到自己的应用服务器上,然后就可以根据 RegistrationID 来向设备推送消息或者通知。
RegistrationID 变化可能性
如果 App 不卸载,是直接覆盖安装,Android, iOS 上 RegistrationID 的值都不会变化。
如果 App 是卸载之后再次安装:
- Android 上 RegistrationID 基本不会变;
 - iOS 上如果启用了 IDFA 变化可能性不大,如果未启用 IDFA 则每次安装 RegistrationID 都会变;
 
RegistrationID 生成规则解析
Android 平台
Android 上因为国内存在大量山寨设备的原因,正常的 IMEI, Mac Address, AndroidID 这些可以考虑用作唯一标识的值,都是不可以用的,因为这些值在一批设备中可能都是同一个值。
极光的基本思路是:
- 生成一个 DeviceID 保存到 Settings, External Storage。依赖本地存储,应用被卸载后重新安装这些存储里的 DeviceID 还在的话,就是同一个设备。这一条理论上解决 90% 的不变性问题。
 - DeviceID 之外增加补充规则:综合根据 IMEI, MAC Address, AndroidID 这几个值来判断,是否可能是老设备。
 
具体的逻辑细节,也是根据实际运行情况,以及收集到的反馈不断调整的,大多数逻辑可在服务器端调整。
iOS平台
鉴于 iOS 系统设计上限制设备唯一标识,所以极光一直使用 Device Token 作为标识,也因为极光推送本身就是需要 Device Token 这个值才可能运作的。
iOS 9 版本之后,每次卸载后重装都会导致 Device Token 变化,所以对于极光后台来说,都只能被识别为新用户。
极光 SDK 新版本增加了 IDFA 选项,在集成初始化 SDK 时可选把 IDFA 这个值设置进来,这样极光后台就优先根据 IDFA 值来识别用户,从有一定的可能性应用被卸载后重装还能识别回老设备。
IDFA 是广告标识符,是 iOS 专门为广告跟踪唯一地识别用户而设计的。在 iOS 设备上,设备 -> 隐私 -> 广告这个页面,有一个设置项:限制广告跟踪。默认是未选中状态的,即是关闭状态,是不限制的。用户可以选中,从而限制广告跟踪。设置项之外还有一个按钮:还原广告标识符…。如果用户点击了这个按钮,则 IDFA 值会变化。
默认的情况下,没有限制广告跟踪,可以取到 IDFA 这个值。并且用户未点击『还原广告标识』时,这个值是不会变的。这样就达到了唯一地标识设备、跟踪到用户的目标。
但是,但是,请一定留意,IDFA 并不是一定可以启用的,是需要你的 App 的确有广告功能才可以用的,否则 Apple 在上架审核时有可能发现从而拒绝上架。
关于苹果 App 上架对 IDFA 的要求,可参考这里的说明:The Advertising Identifier (IDFA)
高级使用建议
因为 RegistrationID 是 JPush SDK 注册完成之后才得到的,所以调用 SDK API 来获取 RegistrationID 的值时需要稍注意,不是总能够立即得到。
比如 iOS 上建议在监听到 kJPFNetworkDidLoginNotification 这个通知后的代码里,来获取 RegistrationID 的值。
Android 上有写 Settings 的权限,是可以写数据到 Settings 里边的,相当于被当作一个永久存储点了。 外部存储一般指 SDCard,现在越来越多手机直接手机自带了,类似于 iPhone 的作法。
这 2 个点如果数据都被破坏了,就的确通过本地 DeviceID 存储无法确认为唯一设备了。但是,服务器端还可以通过 IMEI/AndroidID 这些灵活地确认到是否同一设备。
以上转自 http://blog.jiguang.cn/registrationid/
稳定获取Android设备唯一码(UUID)的解决方案的更多相关文章
- Android设备唯一码的获取
		
Android设备唯一码的获取 UTDID是集团无线设备统一ID方案,目的是给每一台设备一个ID,作为唯一标识.UTDID由客户端生成,并在设备中各个客户端之间共享.UTDID的生成中包含时间戳和随机 ...
 - 获取Android设备唯一标识码
		
概述 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于Android系统版本.厂商定制系统中的Bug等限制,稳定性和唯一 ...
 - android 设备唯一码的获取,Cpu号,Mac地址
		
开发Android应用中,我们常常需要设备的唯一码来确定客户端. Android 中的几中方法,使用中常常不可靠 1. DEVICE_ID 假设我们确实需要用到真实设备的标识,可能就需要用到DEVIC ...
 - android设备唯一码的获取,cpu号,mac地址
		
抄自http://blog.csdn.net/hpccn/article/details/7872141 开发Android应用中,我们常常需要设备的唯一码来确定客户端. Android 中的几中方法 ...
 - Android 获取手机(ios,android)的设备唯一码(mac地址, IMEI)
		
{ /*获取手机(ios,android)的设备唯一码(mac地址, IMEI)2018年02月16日 ⁄ 综合 ⁄ 共 2697字 ⁄ 字号 小 中 大 ⁄ 评论关闭 app中总会用到客户端下载量数 ...
 - 获取设备唯一标识 uuid(采用第三方库SSKeychain)
		
SSKeyChain 下载链接: http://pan.baidu.com/s/1booV3VD 密码: ivdi /** * 获取设备唯一标识 uuid */ +(NSString*) uuid ...
 - 获取iOS设备唯一标识
		
[获取iOS设备唯一标识] 1.已禁用-[UIDevice uniqueIdentifier] 苹果总是把用户的隐私看的很重要.-[UIDevice uniqueIdentifier]在iOS5实际在 ...
 - 获取Android设备屏幕分辨率
		
1.Android 4.3引入的wm工具: a.获取Android设备屏幕分辨率: adb shell wm size b.获取android设备屏幕密度: adb shell wm density ...
 - Android 开发 获取Android设备的屏幕高宽
		
获得屏幕的宽度和高度有很多种方法: //1.通过WindowManager获取 DisplayMetrics dm = new DisplayMetrics(); heigth = dm.height ...
 
随机推荐
- 【C#】Using的一个比较好的语言文字解释
			
其实很早就开始使用using了.但是对这个语法糖我自己一直没有总结也没有一个很好的文字描述解释.今天看其他的博文的时候发现有人对其做了简单的解释我觉得很好,很适合一种讲解.于是抄录下来 using ( ...
 - Dockerize a .NET Core application
			
Dockerize a .NET Core application Introduction This example demonstrates how to dockerize an ASP.NET ...
 - codeforce 886C Petya and Catacombs (map,思路)
			
突然发现百度不到这题的单独题解(果然是因为这是水题么),那我就来写一个了~ 先把题给贴了. C. Petya and Catacombs time limit per test 1 second me ...
 - 什么是java OOM?如何分析及解决oom问题?
			
最近查找了很多关于OOM,甚至于Java内存管理以及JVM的相关资料,发现这方面的东西太多了,竟有一种眼花缭乱的感觉,要想了解全面的话,恐非一篇文章能说清的,因此按照自己的理解整理了一篇,剩下的还需要 ...
 - 【Python】yield
			
彻底理解Python中的yield 2017年04月21日 17:49:57 阅读数:19733 阅读别人的python源码时碰到了这个yield这个关键字,各种搜索终于搞懂了,在此做一下总结: 通常 ...
 - Flexbox弹性布局,更优雅的布局
			
Flexbox,更优雅的布局 Flex 布局教程:语法篇 Flex 布局教程:实例篇 2009年,W3C提出了一种新的方案----Flex布局,可以简便.完整.响应式地实现各种页面布局.目前,它已经得 ...
 - 学习笔记9—python数据表的合并(join(), merge()和concat())
			
merage# pandas提供了一个类似于关系数据库的连接(join)操作的方法<Strong>merage</Strong>,可以根据一个或多个键将不同DataFrame中 ...
 - PHP添加Memcached扩展
			
1.下载memcached扩展 https://pecl.php.net/package/memcache 2.tar -xzvf memcache-2.2.7.tgz #解压memcached ...
 - python如何判断一个字符串是中文,还是英文。
			
参考链接: https://blog.csdn.net/hit0803107/article/details/52885702 decode: 将其它编码转成 ===>unicode enc ...
 - HTML第八章总结
			
Expanding your vocabulary 总述 在上一章节介绍了 CSS 的基础之后,这一章节更加具体地展开关于 CSS 的各种 rules 能够达成的效果.比如 字体:font-famil ...