详解Android获取设备唯一ID的几种方式

先来看看几种比较单一的方式:

IMEI

方式:TelephonyManager.getDeviceId():

问题

  1. 范围:只能支持拥有通话功能的设备,对于平板不可以。
  2. 持久性:返厂,数据擦除的时候不彻底,保留了原来的标识。
  3. 权限:需要权限:Android.permission.READ_PHONE_STATE
  4. bug: 有些厂家的实现有bug,返回一些不可用的数据

 Mac地址

ACCESS_WIFI_STATE权限

有些设备没有WiFi,或者蓝牙,就不可以,如果WiFi没有打开,硬件也不会返回Mac地址,不建议使用

ANDROID_ID
2.2(Froyo,8)版本系统会不可信,来自主要生产厂商的主流手机,至少有一个普遍发现的bug,这些有问题的手机相同的ANDROID_ID: 9774d56d682e549c

但是如果返厂的手机,或者被root的手机,可能会变

Serial Number

从Android 2.3 (“Gingerbread”)开始可用,可以通过android.os.Build.SERIAL获取,对于没有通话功能的设备,它会返回一个唯一的device ID,

以下几个是stackoverflow上评论较多的几个,没贴完,还有其他,综合的,用到以上的部分方式:

地址:http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id

有兴趣的朋友可以再仔细看看

支持率比较高的(支持票数157):androidID --> 剔除2.2版本(API 8)中有问题的手机,使用UUID替代

import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager; 

import java.io.UnsupportedEncodingException;
import java.util.UUID; 

public class DeviceUuidFactory { 

  protected static final String PREFS_FILE = "device_id.xml";
  protected static final String PREFS_DEVICE_ID = "device_id";
  protected static volatile UUID uuid; 

  public DeviceUuidFactory(Context context) {
    if (uuid == null) {
      synchronized (DeviceUuidFactory.class) {
        if (uuid == null) {
          final SharedPreferences prefs = context
              .getSharedPreferences(PREFS_FILE, 0);
          final String id = prefs.getString(PREFS_DEVICE_ID, null);
          if (id != null) {
            // Use the ids previously computed and stored in the
            // prefs file
            uuid = UUID.fromString(id);
          } else {
            final String androidId = Secure.getString(
              context.getContentResolver(), Secure.ANDROID_ID);
            // Use the Android ID unless it's broken, in which case
            // fallback on deviceId,
            // unless it's not available, then fallback on a random
            // number which we store to a prefs file
            try {
              if (!"9774d56d682e549c".equals(androidId)) {
                uuid = UUID.nameUUIDFromBytes(androidId
                    .getBytes("utf8"));
              } else {
                final String deviceId = ((TelephonyManager)
                    context.getSystemService(
                      Context.TELEPHONY_SERVICE)
                      .getDeviceId();
                uuid = deviceId != null ? UUID
                    .nameUUIDFromBytes(deviceId
                        .getBytes("utf8")) : UUID
                    .randomUUID();
              }
            } catch (UnsupportedEncodingException e) {
              throw new RuntimeException(e);
            }
            // Write the value out to the prefs file
            prefs.edit()
                .putString(PREFS_DEVICE_ID, uuid.toString())
                .commit();
          }
        }
      }
    }
  } 

  /**
   * Returns a unique UUID for the current android device. As with all UUIDs,
   * this unique ID is "very highly likely" to be unique across all Android
   * devices. Much more so than ANDROID_ID is.
   *
   * The UUID is generated by using ANDROID_ID as the base key if appropriate,
   * falling back on TelephonyManager.getDeviceID() if ANDROID_ID is known to
   * be incorrect, and finally falling back on a random UUID that's persisted
   * to SharedPreferences if getDeviceID() does not return a usable value.
   *
   * In some rare circumstances, this ID may change. In particular, if the
   * device is factory reset a new device ID may be generated. In addition, if
   * a user upgrades their phone from certain buggy implementations of Android
   * 2.2 to a newer, non-buggy version of Android, the device ID may change.
   * Or, if a user uninstalls your app on a device that has neither a proper
   * Android ID nor a Device ID, this ID may change on reinstallation.
   *
   * Note that if the code falls back on using TelephonyManager.getDeviceId(),
   * the resulting ID will NOT change after a factory reset. Something to be
   * aware of.
   *
   * Works around a bug in Android 2.2 for many devices when using ANDROID_ID
   * directly.
   *
   * @see http://code.google.com/p/android/issues/detail?id=10603
   *
   * @return a UUID that may be used to uniquely identify your device for most
   *     purposes.
   */
  public UUID getDeviceUuid() {
    return uuid;
  }
}

根据版本进行判断的方式:Serial序列号-->UUID (支持数31)

通过Serial 即可,在覆盖率上,你已经成功的获得了98.4%的用户,剩下的1.6%的用户系统是在9 以下的。

通过AndroidID获取,前面已经说过,在8上,有些商家的手机会有一些bug,返回相同的AndroidID,如果Serial和AndroidID都不行

/**
 * Return pseudo unique ID
 * @return ID
 */
public static String getUniquePsuedoID()
{
  // If all else fails, if the user does have lower than API 9 (lower
  // than Gingerbread), has reset their phone or 'Secure.ANDROID_ID'
  // returns 'null', then simply the ID returned will be solely based
  // off their Android device information. This is where the collisions
  // can happen.
  // Thanks http://www.pocketmagic.net/?p=1662!
  // Try not to use DISPLAY, HOST or ID - these items could change.
  // If there are collisions, there will be overlapping data
  String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10); 

  // Thanks to @Roman SL!
  // http://stackoverflow.com/a/4789483/950427
  // Only devices with API >= 9 have android.os.Build.SERIAL
  // http://developer.android.com/reference/android/os/Build.html#SERIAL
  // If a user upgrades software or roots their phone, there will be a duplicate entry
  String serial = null;
  try
  {
    serial = android.os.Build.class.getField("SERIAL").get(null).toString(); 

    // Go ahead and return the serial for api => 9
    return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
  }
  catch (Exception e)
  {
    // String needs to be initialized
    serial = "serial"; // some value
  } 

  // Thanks @Joe!
  // http://stackoverflow.com/a/2853253/950427
  // Finally, combine the values we have found by using the UUID class to create a unique identifier
  return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}

不用READ_PHONE_STATE权限直接获取ROM信息的方式:(支持率较低 16)

String m_szDevIDShort = "35" + //we make this look like a valid IMEI
      Build.BOARD.length()%10+ Build.BRAND.length()%10 +
      Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 +
      Build.DISPLAY.length()%10 + Build.HOST.length()%10 +
      Build.ID.length()%10 + Build.MANUFACTURER.length()%10 +
      Build.MODEL.length()%10 + Build.PRODUCT.length()%10 +
      Build.TAGS.length()%10 + Build.TYPE.length()%10 +
      Build.USER.length()%10 ; //13 digits 

最后贴上自己在项目中用的:

public static String getDeviceId(Context context) {
    String deviceId = "";
    if (deviceId != null && !"".equals(deviceId)) {
     return deviceId;
   }
    if (deviceId == null || "".equals(deviceId)) {
      try {
        deviceId = getLocalMac(context).replace(":", "");
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    if (deviceId == null || "".equals(deviceId)) {
      try {
        deviceId = getAndroidId(context);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    if (deviceId == null || "".equals(deviceId)) { 

      if (deviceId == null || "".equals(deviceId)) {
        UUID uuid = UUID.randomUUID();
        deviceId = uuid.toString().replace("-", "");
        writeDeviceID(deviceId);
      }
    }
    return deviceId;
  } 
// IMEI码
  private static String getIMIEStatus(Context context) {
    TelephonyManager tm = (TelephonyManager) context
        .getSystemService(Context.TELEPHONY_SERVICE);
    String deviceId = tm.getDeviceId();
    return deviceId;
  } 

  // Mac地址
  private static String getLocalMac(Context context) {
    WifiManager wifi = (WifiManager) context
        .getSystemService(Context.WIFI_SERVICE);
    WifiInfo info = wifi.getConnectionInfo();
    return info.getMacAddress();
  } 

  // Android Id
  private static String getAndroidId(Context context) {
    String androidId = Settings.Secure.getString(
        context.getContentResolver(), Settings.Secure.ANDROID_ID);
    return androidId;
  } 

  public static void saveDeviceID(String str) {
    try {
      FileOutputStream fos = new FileOutputStream(file);
      Writer out = new OutputStreamWriter(fos, "UTF-8");
      out.write(str);
      out.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  } 

  public static String readDeviceID() {
    StringBuffer buffer = new StringBuffer();
    try {
      FileInputStream fis = new FileInputStream(file);
      InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
      Reader in = new BufferedReader(isr);
      int i;
      while ((i = in.read()) > -1) {
        buffer.append((char) i);
      }
      in.close();
      return buffer.toString();
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    }
  }

对于获取设备唯一ID并没有绝对的方案,这一点在android的官方博客中也提到了,不过以上几种方案,应该可以满足平时的需求,大家可以选择其中自己认为比较好的,用于自己的项目中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android 获取设备屏幕大小的几种方法总结

    1.通过WindowManager获取 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); System.out.println("heigth : " + dm.heightPixels); System.out.println("width : " + dm.widthPixels); 2.通过Resources获取 Di

  • Android中获取设备的各种信息总结

    一.屏幕分辨率 Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; int height = size.y; 或者: DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getM

  • android判断设备是否有相机的实例代码

    通过PackageManager可以判断android设备是否有相机 PackageManager pm = getPackageManager(); // FEATURE_CAMERA - 后置相机 // FEATURE_CAMERA_FRONT - 前置相机 if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) && !pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT

  • Android应用获取设备序列号的方法

    软硬件环境 Macbook Pro MGX 72 Android studio 2.1.2 Android 5.1.1 前言 上一篇介绍了如何获取ethernet的MAC地址,对于厂商来讲,除了MAC号,还有一项数据也很重要,那就是机器序列号,它是机器出厂时确定的,也是主要标识,每台机器都不一样. 要想获取到序列号,必须要先知道序列号从哪里来,写到了哪里.一般情况下,序列号都是厂商自己定义的一串字串,字串中的某几位会有不同含义,比如厂商的ID.出厂时间.机器类型等,另外,运营商定义的序列号几乎也

  • Android6.0蓝牙出现无法扫描设备或闪退问题解决办法

    Android6.0蓝牙出现无法扫描设备或闪退问题解决办法 前言: 目前待的这家公司是做智能家居的,最近客户那边有反馈说为什么我的手机蓝牙也打开了,设备的蓝牙也打开了,为啥总是扫描不到呢,但是我们公司的测试人员几经排查,并未发现客户的所描述的扫描不到设备,但客户所说的问题确实又存在,几经周折,找到了原因,原来是现在市场上出来的android6.0手机需要添加两个权限,Android官网也已经说明了, 直接上图 具体权限官网说的很清楚了 解决办法 Android6.0设备通过蓝牙和Wi-Fi扫描访

  • 如何从UA分辨出Android设备类型

    随着Android设备增多,不少网站都开始设备Android设备,而Android主流设备类型以手机和平板为主.网站在适配时通过User Agent(用户代理,以下简称UA)又如何区分呢,本文部分内容翻译自Google官方博客Mo' better to also detect "mobile" user-agent. 一针见血 标准判断规则:Mobile Android has "Mobile" string in the User-Agent header. Ta

  • Android编程获取设备MAC地址的实现方法

    本文实例讲述了Android编程获取设备MAC地址的实现方法.分享给大家供大家参考,具体如下: /** * 获取设备的mac地址 * * @param ac * @param callback * 成功获取到mac地址之后会回调此方法 */ public static void getMacAddress(final Activity ac, final SimpleCallback callback) { final WifiManager wm = (WifiManager) ac .get

  • android设备不识别awk命令 缺少busybox怎么办

    android设备不识别awk命令,缺少busybox 一.什么是BusyBox ? BusyBox 是标准 Linux 工具的一个单个可执行实现.BusyBox 包含了一些简单的工具,例如 cat 和 echo,还包含了一些更大.更复杂的工具,例如 grep.find.mount 以及 telnet.有些人将 BusyBox 称为 Linux 工具里的瑞士军刀.简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令.(摘自百度百科) 二.下载BusyBox 下载B

  • 详解Android获取设备唯一ID的几种方式

    先来看看几种比较单一的方式: IMEI 方式:TelephonyManager.getDeviceId(): 问题 范围:只能支持拥有通话功能的设备,对于平板不可以. 持久性:返厂,数据擦除的时候不彻底,保留了原来的标识. 权限:需要权限:Android.permission.READ_PHONE_STATE bug: 有些厂家的实现有bug,返回一些不可用的数据  Mac地址 ACCESS_WIFI_STATE权限 有些设备没有WiFi,或者蓝牙,就不可以,如果WiFi没有打开,硬件也不会返回

  • 详解Android获取所有依赖库的几种方式

    当项目越来越大的时候,依赖的库也越来越多,再加上aar的传递依赖,导致dependency的急速膨胀.我们可以通过如下几种方式,查看项目依赖的所有库(包含直接依赖和间接依赖). 方式一:通过dependencies命令 ./gradlew :app:dependencies 该task会显示如下所示的输出: 输出列表展示了所有configuration下的依赖树,依赖关系明显,层次清晰.如果觉得输出的结果太冗长(通常情况下包含几十个configuration),可以通过指定configurati

  • 详解Python获取线程返回值的三种方式

    目录 方法一 方法二 方法三 最后的话 提到线程,你的大脑应该有这样的印象:我们可以控制它何时开始,却无法控制它何时结束,那么如何获取线程的返回值呢?今天就分享一下自己的一些做法. 方法一 使用全局变量的列表,来保存返回值 ret_values = [] def thread_func(*args):     ...     value = ...     ret_values.append(value) 选择列表的一个原因是:列表的 append() 方法是线程安全的,CPython 中,GI

  • 详解android进行异步更新UI的四种方式

    大家都知道由于性能要求,Android要求只能在UI线程中更新UI,要想在其他线程中更新UI,我大致总结了4种方式,欢迎补充纠正: 使用Handler消息传递机制: 使用AsyncTask异步任务: 使用runOnUiThread(action)方法: 使用Handler的post(Runnabel r)方法: 下面分别使用四种方式来更新一个TextView. 1.使用Handler消息传递机制 package com.example.runonuithreadtest; import andr

  • 详解android与服务端交互的两种方式

    做Android开发的程序员必须知道android客户端应该如何与服务端进行交互,这里主要介绍的是使用json数据进行交互.服务端从数据库查出数据并以json字符串的格式或者map集合的格式返回到客户端,客户端进行解析并输出到手机屏幕上. 此处介绍两种方式:使用Google原生的Gson解析json数据,使用JSONObject解析json数据 一.使用Google原生的Gson解析json数据: 记得在客户端添加gson.jar. 核心代码: 服务端: package com.mfc.ctrl

  • 详解Android提交数据到服务器的两种方式四种方法

    Android应用开发中,会经常要提交数据到服务器和从服务器得到数据,本文主要是给出了利用http协议采用HttpClient方式向服务器提交数据的方法. 代码比较简单,这里不去过多的阐述,直接看代码. /** * @author Dylan * 本类封装了Android中向web服务器提交数据的两种方式四种方法 */ public class SubmitDataByHttpClientAndOrdinaryWay { /** * 使用get请求以普通方式提交数据 * @param map 传

  • 浅谈android获取设备唯一标识完美解决方案

    本文介绍了浅谈android获取设备唯一标识完美解决方案,分享给大家,具体如下: /** * deviceID的组成为:渠道标志+识别符来源标志+hash后的终端识别符 * * 渠道标志为: * 1,andriod(a) * * 识别符来源标志: * 1, wifi mac地址(wifi): * 2, IMEI(imei): * 3, 序列号(sn): * 4, id:随机码.若前面的都取不到时,则随机生成一个随机码,需要缓存. * * @param context * @return */ p

  • 详解Android GLide图片加载常用几种方法

    目录 缓存浅析 GLide图片加载方法 图片加载周期 图片格式(Bitmap,Gif) 缓存 集成网络框架 权限 占位符 淡入效果 变换 启动页/广告页 banner 固定宽高 圆角 圆形 总结 缓存浅析 为啥要做缓存? android默认给每个应用只分配16M的内存,所以如果加载过多的图片,为了 防止内存溢出 ,应该将图片缓存起来. 图片的三级缓存分别是: 1.内存缓存 2.本地缓存 3.网络缓存 其中,内存缓存应优先加载,它速度最快:本地缓存次优先加载,它速度也快:网络缓存不应该优先加载,它

  • 详解pandas获取Dataframe元素值的几种方法

    可以通过遍历的方法: pandas按行按列遍历Dataframe的几种方式:https://www.jb51.net/article/172623.htm 选择列 使用类字典属性,返回的是Series类型 data['w'] 遍历Series for index in data['w'] .index: time_dis = data['w'] .get(index) pandas.DataFrame.at 根据行索引和列名,获取一个元素的值 >>> df = pd.DataFrame(

  • 详解Java中数组判断元素存在几种方式比较

    1. 通过将数组转换成List,然后使用List中的contains进行判断其是否存在 public static boolean useList(String[] arr,String containValue){ return Arrays.asList(arr).contains(containValue); } 需要注意的是Arrays.asList这个方法中转换的List并不是java.util.ArrayList而是java.util.Arrays.ArrayList,其中java.

随机推荐