zoukankan      html  css  js  c++  java
  • 稳定获取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 值会变化。

    2115454-b0c648ae038d8d0a
     

    默认的情况下,没有限制广告跟踪,可以取到 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/

  • 相关阅读:
    软考倒计时2天
    案例分析
    冒泡排序
    二分查找
    MySQL 错误 1366:1366 Incorrect integer value
    linux(centos) 添加系统环境变量
    php的opcache缓存扩展
    启动mysqld报 mysql the server quit without updating pid file
    mysql5.5 报Can't open and lock privilege tables: Table 'mysql.host' doesn't exist
    关于git CRLF LF结尾的问题
  • 原文地址:https://www.cnblogs.com/xd502djj/p/8137204.html
Copyright © 2011-2022 走看看