zoukankan      html  css  js  c++  java
  • 【转】 android获取设备唯一标识完美解决方案

    [java] view plain copy
     
     print?
    1. <p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px; line-height: 25.2px;"><span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"> /**</span></p>  * deviceID的组成为:渠道标志+识别符来源标志+hash后的终端识别符 
    2.   * 
    3.   * 渠道标志为: 
    4.   * 1,andriod(a) 
    5.   * 
    6.   * 识别符来源标志: 
    7.   * 1, wifi mac地址(wifi); 
    8.   * 2, IMEI(imei); 
    9.   * 3, 序列号(sn); 
    10.   * 4, id:随机码。若前面的都取不到时,则随机生成一个随机码,需要缓存。 
    11.   * 
    12.   * @param context 
    13.   * @return 
    14.   */  
    15. public static String getDeviceId(Context context) {  
    16.   StringBuilder deviceId = new StringBuilder();  
    17.   // 渠道标志  
    18.   deviceId.append("a");  
    19.   try {  
    20.     //wifi mac地址  
    21.     WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);  
    22.     WifiInfo info = wifi.getConnectionInfo();  
    23.     String wifiMac = info.getMacAddress();  
    24.     if(!isEmpty(wifiMac)){  
    25.       deviceId.append("wifi");  
    26.       deviceId.append(wifiMac);  
    27.       PALog.e("getDeviceId : ", deviceId.toString());  
    28.       return deviceId.toString();  
    29.     }  
    30.     //IMEI(imei)  
    31.     TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);  
    32.     String imei = tm.getDeviceId();  
    33.     if(!isEmpty(imei)){  
    34.       deviceId.append("imei");  
    35.       deviceId.append(imei);  
    36.       PALog.e("getDeviceId : ", deviceId.toString());  
    37.       return deviceId.toString();  
    38.     }  
    39.     //序列号(sn)  
    40.     String sn = tm.getSimSerialNumber();  
    41.     if(!isEmpty(sn)){  
    42.       deviceId.append("sn");  
    43.       deviceId.append(sn);  
    44.       PALog.e("getDeviceId : ", deviceId.toString());  
    45.       return deviceId.toString();  
    46.     }  
    47.     //如果上面都没有, 则生成一个id:随机码  
    48.     String uuid = getUUID(context);  
    49.     if(!isEmpty(uuid)){  
    50.       deviceId.append("id");  
    51.       deviceId.append(uuid);  
    52.       PALog.e("getDeviceId : ", deviceId.toString());  
    53.       return deviceId.toString();  
    54.     }  
    55.   } catch (Exception e) {  
    56.     e.printStackTrace();  
    57.     deviceId.append("id").append(getUUID(context));  
    58.   }  
    59.   PALog.e("getDeviceId : ", deviceId.toString());  
    60.   return deviceId.toString();  
    61. }  
    62. /** 
    63.  * 得到全局唯一UUID 
    64.  */  
    65. public static String getUUID(Context context){  
    66.   SharedPreferences mShare = getSysShare(context, "sysCacheMap");  
    67.   if(mShare != null){  
    68.     uuid = mShare.getString("uuid", "");  
    69.   }  
    70.   if(isEmpty(uuid)){  
    71.     uuid = UUID.randomUUID().toString();  
    72.     saveSysMap(context, "sysCacheMap", "uuid", uuid);  
    73.   }  
    74.   PALog.e(tag, "getUUID : " + uuid);  
    75. return uuid;  
    76. }  

    有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码。虽然Android系统中提供了这样设备识别码,但是由于Android系统版本、厂商定制系统中的Bug等限制,稳定性和唯一性并不理想。而通过其他硬件信息标识也因为系统版本、手机硬件等限制存在不同程度的问题。

    下面收集了一些“有能力”或“有一定能力”作为设备标识的串码。

    DEVICE_ID

    这是Android系统为开发者提供的用于标识手机设备的串号,也是各种方法中普适性较高的,可以说几乎所有的设备都可以返回这个串号,并且唯一性良好。

    这个DEVICE_ID可以同通过下面的方法获取:

    [java] view plain copy
     
     print?
    1. TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);  
    2. String DEVICE_ID = tm.getDeviceId();   

    假设我们确实需要用到真实设备的标识,可能就需要用到DEVICE_ID。在以前,我们的Android设备是手机,这个DEVICE_ID可以同通过TelephonyManager.getDeviceId()获取,它根据不同的手机设备返回IMEI,MEID或者ESN码,但它在使用的过程中会遇到很多问题:

    • 非手机设备: 如果只带有Wifi的设备或者音乐播放器没有通话的硬件功能的话就没有这个DEVICE_ID
    • 权限: 获取DEVICE_ID需要READ_PHONE_STATE权限,但如果我们只为了获取它,没有用到其他的通话功能,那这个权限有点大才小用
    • bug:在少数的一些手机设备上,该实现有漏洞,会返回垃圾,如:zeros或者asterisks的产品

    MAC ADDRESS

    可以使用手机Wifi或蓝牙的MAC地址作为设备标识,但是并不推荐这么做,原因有以下两点:

    • 硬件限制:并不是所有的设备都有Wifi和蓝牙硬件,硬件不存在自然也就得不到这一信息。
    • 获取的限制:如果Wifi没有打开过,是无法获取其Mac地址的;而蓝牙是只有在打开的时候才能获取到其Mac地址。

    获取Wifi Mac地址:

    获取蓝牙 Mac地址:

    Sim Serial Number

    装有SIM卡的Android 2.3设备,可以通过下面的方法获取到Sim Serial Number:

    [java] view plain copy
     
     print?
    1. TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);   
    2. String SimSerialNumber = tm.getSimSerialNumber();   

    注意:对于CDMA设备,返回的是一个空值!

    ANDROID_ID

    在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。可以通过下面的方法获取:

    [java] view plain copy
     
     print?
    1. import android.provider.Settings;  
    2. String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);  
    ANDROID_ID可以作为设备标识,但需要注意:
    • 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。
    • 厂商定制系统的Bug:有些设备返回的值为null。
    • 设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
    • 它在Android <=2.1 or Android >=2.3的版本是可靠、稳定的,但在2.2的版本并不是100%可靠的

    Serial Number

    Android系统2.3版本以上可以通过下面的方法得到Serial Number,且非手机设备也可以通过该接口获取。

    [java] view plain copy
     
     print?
    1. String SerialNumber = android.os.Build.SERIAL;   

    以上几种方式都或多或少存在一定的局限性或者Bug,如果并不是确实需要对硬件本身进行绑定,使用自己生成的UUID也是一个不错的选择,因为该方法无需访问设备的资源,也跟设备类型无关。

    Installtion ID

    这种方式的原理是在程序安装后第一次运行时生成一个ID,该方式和设备唯一标识不一样,不同的应用程序会产生不同的ID,同一个程序重新安装也会不同。所以这不是设备的唯一ID,但是可以保证每个用户的ID是不同的。可以说是用来标识每一份应用程序的唯一ID(即Installtion ID),可以用来跟踪应用的安装数量等。

    Google Developer Blog提供了这样的一个框架:

    [java] view plain copy
     
     print?
    1. public class Installation {  
    2.     private static String sID = null;  
    3.     private static final String INSTALLATION = "INSTALLATION";  
    4.   
    5.     public synchronized static String id(Context context) {  
    6.         if (sID == null) {    
    7.             File installation = new File(context.getFilesDir(), INSTALLATION);  
    8.             try {  
    9.                 if (!installation.exists())  
    10.                     writeInstallationFile(installation);  
    11.                 sID = readInstallationFile(installation);  
    12.             } catch (Exception e) {  
    13.                 throw new RuntimeException(e);  
    14.             }  
    15.         }  
    16.         return sID;  
    17.     }  
    18.   
    19.     private static String readInstallationFile(File installation) throws IOException {  
    20.         RandomAccessFile f = new RandomAccessFile(installation, "r");  
    21.         byte[] bytes = new byte[(int) f.length()];  
    22.         f.readFully(bytes);  
    23.         f.close();  
    24.         return new String(bytes);  
    25.     }  
    26.   
    27.     private static void writeInstallationFile(File installation) throws IOException {  
    28.         FileOutputStream out = new FileOutputStream(installation);  
    29.         String id = UUID.randomUUID().toString();  
    30.         out.write(id.getBytes());  
    31.         out.close();  
    32.     }  
    33. }  

    设备唯一ID

    上文可以看出,Android系统中并没有可以可靠获取所有厂商设备唯一ID的方法,各个方法都有自己的使用范围和局限性,这也是目前流行的Android系统版本过多,设备也是来自不同厂商,且没有统一标准等原因造成的。

    从目前发展来看,Android系统多版本共存还会持续较长的时间,而Android系统也不会被某个设备生产厂商垄断,长远看Android基础系统将会趋于稳定,设备标识也将会作为系统基础部分而标准化,届时这一问题才有望彻底解决。

    目前的解决办法,比较可行的是一一适配,在保证大多数设备方便的前提下,如果获取不到,使用其他备选信息作为标识,即自己再封装一个设备ID出来,通过内部算法保证尽量和设备硬件信息相关,以及标识的唯一性。

    总结

    综合以上所述,为了实现在设备上更通用的获取设备唯一标识,我们可以实现这样的一个类,为每个设备产生唯一的UUID,以ANDROID_ID为基础,在获取失败时以TelephonyManager.getDeviceId()为备选方法,如果再失败,使用UUID的生成策略。

    重申下,以下方法是生成Device ID,在大多数情况下Installtion ID能够满足我们的需求,但是如果确实需要用到Device ID,那可以通过以下方式实现:

    [java] view plain copy
     
     print?
    1. import android.content.Context;  
    2. import android.content.SharedPreferences;  
    3. import android.provider.Settings.Secure;  
    4. import android.telephony.TelephonyManager;  
    5. import java.io.UnsupportedEncodingException;  
    6. import java.util.UUID;  
    7.   
    8. public class DeviceUuidFactory {  
    9.     protected static final String PREFS_FILE = "device_id.xml";  
    10.     protected static final String PREFS_DEVICE_ID = "device_id";  
    11.     protected static UUID uuid;  
    12.   
    13.     public DeviceUuidFactory(Context context) {  
    14.         if( uuid ==null ) {  
    15.             synchronized (DeviceUuidFactory.class) {  
    16.                 if( uuid == null) {  
    17.                     final SharedPreferences prefs = context.getSharedPreferences( PREFS_FILE, 0);  
    18.                     final String id = prefs.getString(PREFS_DEVICE_ID, null );  
    19.                     if (id != null) {  
    20.                         // Use the ids previously computed and stored in the prefs file  
    21.                         uuid = UUID.fromString(id);  
    22.                     } else {  
    23.                         final String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);  
    24.                         // Use the Android ID unless it's broken, in which case fallback on deviceId,  
    25.                         // unless it's not available, then fallback on a random number which we store  
    26.                         // to a prefs file  
    27.                         try {  
    28.                             if (!"9774d56d682e549c".equals(androidId)) {  
    29.                                 uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));  
    30.                             } else {  
    31.                                 final String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId();  
    32.                                 uuid = deviceId!=null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID();  
    33.                             }  
    34.                         } catch (UnsupportedEncodingException e) {  
    35.                             throw new RuntimeException(e);  
    36.                         }  
    37.                         // Write the value out to the prefs file  
    38.                         prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString() ).commit();  
    39.                     }  
    40.                 }  
    41.             }  
    42.         }  
    43.     }  
    44.     /** 
    45.      * Returns a unique UUID for the current android device.  As with all UUIDs, this unique ID is "very highly likely" 
    46.      * to be unique across all Android devices.  Much more so than ANDROID_ID is. 
    47.      * 
    48.      * The UUID is generated by using ANDROID_ID as the base key if appropriate, falling back on 
    49.      * TelephonyManager.getDeviceID() if ANDROID_ID is known to be incorrect, and finally falling back 
    50.      * on a random UUID that's persisted to SharedPreferences if getDeviceID() does not return a 
    51.      * usable value. 
    52.      * 
    53.      * In some rare circumstances, this ID may change.  In particular, if the device is factory reset a new device ID 
    54.      * may be generated.  In addition, if a user upgrades their phone from certain buggy implementations of Android 2.2 
    55.      * to a newer, non-buggy version of Android, the device ID may change.  Or, if a user uninstalls your app on 
    56.      * a device that has neither a proper Android ID nor a Device ID, this ID may change on reinstallation. 
    57.      * 
    58.      * Note that if the code falls back on using TelephonyManager.getDeviceId(), the resulting ID will NOT 
    59.      * change after a factory reset.  Something to be aware of. 
    60.      * 
    61.      * Works around a bug in Android 2.2 for many devices when using ANDROID_ID directly. 
    62.      * 
    63.      * @see http://code.google.com/p/android/issues/detail?id=10603 
    64.      * 
    65.      * @return a UUID that may be used to uniquely identify your device for most purposes. 
    66.      */  
    67.     public UUID getDeviceUuid() {  
    68.         return uuid;  
    69.     }  
    70. }  
    如何获取Android手机的唯一标识?

    代码: 这里是你在Android里读出 唯一的 IMSI-ID / IMEI-ID 的方法。

    Java: 
    [java] view plain copy
     
     print?
    1. String myIMSI = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMSI);   
    2. // within my emulator it returns: 310995000000000   
    3.   
    4. String myIMEI = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMEI);   
    5. // within my emulator it returns: 000000000000000   
    注:android.os.SystemProperties的标签被打上@hide了,所以sdk中并不会存在。如果需要使用,需要有android的source code支持。
     
    from:http://blog.csdn.net/aa1733519509/article/details/50053553
  • 相关阅读:
    对MVC模型的自悟,详尽解释,为了更多非计算机人员可以理解
    openSUSE leap 42.3 实现有线 无线同时用
    Fedora27 源配置
    Ubuntu16.04添加HP Laserjet Pro M128fn打印机和驱动
    openSUSE leap 42.3 添加HP Laserjet Pro M128fn打印机和驱动
    OpenSUSE Leap 42.3下通过Firefox Opera Chromium浏览器直接执行java应用程序(打开java jnlp文件)实现在服务器远程虚拟控制台完成远程管理的方法
    OpenSUSE Leap 42.3 安装java(Oracle jre)
    linux下支持托盘的邮件客户端Sylpheed
    Ubuntu下通过Firefox Opera Chromium浏览器直接执行java应用程序(打开java jnlp文件)实现在服务器远程虚拟控制台完成远程管理的方法
    Firefox 浏览器添加Linux jre插件
  • 原文地址:https://www.cnblogs.com/xuan52rock/p/6530531.html
Copyright © 2011-2022 走看看