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可以同通过下面的方法获取:

  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:

  1. TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
  2. String SimSerialNumber = tm.getSimSerialNumber();

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

ANDROID_ID

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

  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,且非手机设备也可以通过该接口获取。

  1. String SerialNumber = android.os.Build.SERIAL;

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

Installtion ID

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

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

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

设备唯一ID

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

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

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

总结

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

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

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

如何获取Android手机的唯一标识?

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

Java:

  1. String myIMSI = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMSI);
  2. // within my emulator it returns: 310995000000000
  3. String myIMEI = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMEI);
  4. // within my emulator it returns: 000000000000000

注:android.os.SystemProperties的标签被打上@hide了,所以sdk中并不会存在。如果需要使用,需要有android的source code支持。

 
from:http://blog.csdn.net/aa1733519509/article/details/50053553

【转】 android获取设备唯一标识完美解决方案的更多相关文章

  1. android 获取设备唯一标识完美解决方案

    /** * deviceID的组成为:渠道标志+识别符来源标志+hash后的终端识别符 * * 渠道标志为: * 1,andriod(a) * * 识别符来源标志: * 1, wifi mac地址(w ...

  2. Android 获取设备唯一标识码

    概述 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于Android系统版本.厂商定制系统中的Bug等限制,稳定性和唯一 ...

  3. ios开发——实用技术篇OC篇&获取设备唯一标识

    获取设备唯一标识 WWDC 2013已经闭幕,IOS7 Beta随即发布,界面之难看无以言表...,简直就是山寨Android. 更让IOS程序猿悲催的是,设备唯一标识的MAC Address在IOS ...

  4. android获取设备唯一标示

    概述 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于Android系统版本.厂商定制系统中的Bug等限制,稳定性和唯一 ...

  5. DeviceUuidFactory【获取设备唯一标识码的UUID(加密)】【需要运行时权限的处理的配合】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于An ...

  6. IOS7如何获取设备唯一标识

    WWDC 2013已经闭幕,IOS7 Beta随即发布,界面之难看无以言表...,简直就是山寨Android. 更让IOS程序猿悲催的是,设备唯一标识的MAC Address在IOS7中也失效了. I ...

  7. iOS获取设备唯一标识的8种方法

    8种iOS获取设备唯一标识的方法,希望对大家有用. UDID UDID(Unique Device Identifier),iOS 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可 ...

  8. 获取设备唯一标识 uuid(采用第三方库SSKeychain)

    SSKeyChain 下载链接: http://pan.baidu.com/s/1booV3VD 密码: ivdi /** *  获取设备唯一标识 uuid */ +(NSString*) uuid ...

  9. iOS获取设备唯一标识的各种方法?IDFA、IDFV、UDID分别是什么含义?

    一.UDID (Unique Device Identifier) UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和 ...

随机推荐

  1. 配置Windows 防火墙,允许SQL Server的远程连接

    在运行SQL Server的服务器上,我们要找到哪些是SQL Server正在侦听的端口,并将其添加到Windows防火墙的入站例外. 首先,我们需要添加 SQL Server 服务侦听 Window ...

  2. ExtJs 中Viewport的介绍与使用

    ExtJs 中Viewport的介绍与使用 VeiwPort 代表整个浏览器显示区域,该对象渲染到页面的body 区域,并会随着浏览器显示区域的大小自动改变,一个页面中只能有一个ViewPort 实例 ...

  3. Sql Server 与 MySql 在使用 update inner join 时的区别

    Sql Server -- 不使用别名 UPDATE tb_User SET tb_User.pass = '' FROM tb_User usr INNER JOIN tb_Address addr ...

  4. Springmvx拦截html出现406解决以及Server Tomcat v8.0 Server at localhost failed to start 问题解决方法

    问题是这样的:环境是SSM框架,在配置好的框架里想请求一个html,结果406了,406就是HTTP协议状态码的一种,表示无法使用请求的特性来响应请求的网页.一般指客户端浏览器不接受所请求页面的MIM ...

  5. 猜生日 Java小游戏

    最近看到一个很有趣的小游戏: 询问朋友5个问题,找到他出生在一个月的哪一天.每个问题都是询问他的生日是否是5个数字集合中的一个. 这5个集合分别是: set1:1 3 5 7 9 11 13 15 1 ...

  6. Java的策略模式

    策略模式也是我们经常使用的模式,它的定义如下:将可变的部分从程序中抽象分离出来成为算法接口,在该部分下分别封装一系列算法实现并使它们可以相互替换. 举个例子,我们现在有三种支付方式:支付宝支付,微信支 ...

  7. 新建hadoop用户以及用户组,给予sudo权限

    1.首先新建用户,adduser命令 sudo adduser hadoop passwd hadoop 输入密码之后,一路 y 确定. 2.添加用户组 在创建hadoop用户的同时也创建了hadoo ...

  8. django-缓存的应用

    为什么需要缓存? django中文文档: 通常,计算值是昂贵的(即资源匮乏和缓慢),因此将值保存到可快速访问的缓存中可以有巨大的好处,为下一次需要做好准备. 这是一个足够重要和强大的技术,Django ...

  9. python+redis简单实现发红包程序

    redis是什么? Redis 是一个高性能的key-value数据库! 想进一步了解请移步搜索引擎自行查找. 编写这个小程序的目的就是对redis进行一个简单的小操作,对redis有一个初步的了解, ...

  10. agc007D - Shik and Game(dp 单调性)

    题意 题目链接 Sol 主人公的最优决策一定是经过熊->返回到某个位置->收集经过的钻石 那么可以直接设\(f[i]\)表示收集完了前\(i\)个位置的钻石的最小时间,转移的时候枚举下最后 ...