稳定获取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 这些可以考虑用作唯一标识的值,都是不可以用的,因为这些值在一批设备中可能都是同一个值。

极光的基本思路是:

  1. 生成一个 DeviceID 保存到 Settings, External Storage。依赖本地存储,应用被卸载后重新安装这些存储里的 DeviceID 还在的话,就是同一个设备。这一条理论上解决 90% 的不变性问题。
  2. 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/

 

 本文转自茄子_2008博客园博客,原文链接:http://www.cnblogs.com/xd502djj/p/8137204.html,如需转载请自行联系原作者。


你可能感兴趣的:(稳定获取Android设备唯一码(UUID)的解决方案)