协慌网

登录 贡献 社区

是否有唯一的 Android 设备 ID?

Android 设备是否具有唯一的 ID,如果是这样,使用 Java 访问它的简单方法是什么?

答案

Settings.Secure#ANDROID_ID返回 Android ID 作为每个用户 64 位十六进制字符串的唯一 ID。

import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID);

更新 :截至 Android 的最新版本, ANDROID_ID许多问题已得到解决,我相信不再需要这种方法。请看看安东尼的回答

完全披露:我的应用程序最初使用了以下方法但不再使用此方法,我们现在使用Android 开发人员博客条目中概述的方法,即emmby 的答案链接(即生成并保存UUID#randomUUID() )。


这个问题有很多答案,其中大部分只会在某些时候起作用,不幸的是,这还不够好。

基于我对设备的测试(所有电话,其中至少有一部分未激活):

  1. 测试的所有设备都返回TelephonyManager.getDeviceId()的值
  2. 所有 GSM 设备(均使用 SIM 测试)返回TelephonyManager.getSimSerialNumber()的值
  3. 所有 CDMA 设备都为getSimSerialNumber()返回 null(如预期的那样)
  4. 添加了 Google 帐户的所有设备都返回了ANDROID_ID的值
  5. 只要在安装过程中添加了 Google 帐户,所有 CDMA 设备都会为ANDROID_IDTelephonyManager.getDeviceId()返回相同的值(或相同值的推导)。
  6. 我还没有机会测试没有 SIM 卡的 GSM 设备,没有添加 Google 帐户的 GSM 设备,或飞机模式下的任何设备。

所以,如果你想要独一无二的设备本身,一些TM.getDeviceId() 应该足够了。显然有些用户比其他用户更偏执,因此对这些标识符中的一个或多个进行散列可能很有用,这样该字符串对于设备来说实际上仍然是唯一的,但是没有明确标识用户的实际设备。例如,使用String.hashCode() ,结合 UUID:

final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);

final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
tmSerial = "" + tm.getSimSerialNumber();
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();

可能会导致类似于: 00000000-54b3-e7c7-0000-000046bffd97

它对我来说效果很好。

正如 Richard 在下面提到的那样,不要忘记您需要获得读取TelephonyManager属性的权限,因此请将其添加到清单中:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

导入库

import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;

最后更新时间:2015 年 6 月 2 日


在阅读关于创建唯一 ID,Google 开发人员博客和 Android 文档的每篇 Stack Overflow 帖子后,我觉得好像'Pseudo ID' 是最好的选择。

主要问题:硬件与软件

硬件

  • 用户可以更改他们的硬件,Android 平板电脑或手机,因此基于硬件的唯一 ID 不是跟踪用户的好主意
  • 对于TRACKING HARDWARE ,这是一个好主意

软件

  • 如果 root 用户可以擦除 / 更改他们的 ROM
  • 您可以跨平台(iOS,Android,Windows 和 Web)跟踪用户
  • 最好的想要在他们同意的情况下 追踪个人用户只需让他们登录(使用 OAuth 使其无缝)

Android 的总体细分

- 保证 API> = 9/10 的唯一性(包括 root 设备)(99.5%的 Android 设备)

- 没有额外的权限

Psuedo 代码:

if API >= 9/10: (99.5% of devices)

return unique ID containing serial id (rooted devices may be different)

else

return unique ID of build information (may overlap data - API < 9)

感谢 @stansult 发布我们的所有选项 (在此 Stack Overflow 问题中)。

选项列表 - 原因 / 为何不使用它们:

  • 用户电子邮件 - 软件

    • 用户可以更改电子邮件 - 极不可能
    • API 5+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    • API 14+ <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" />如何获取 Android 设备的主要电子邮件地址
  • 用户电话号码 - 软件

    • 用户可以更改电话号码 - 极不可能
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI - 硬件 (只有手机,需要android.permission.READ_PHONE_STATE

    • 大多数用户讨厌在许可中说 “电话” 的事实。一些用户给出了不好的评级,因为他们认为你只是窃取他们的个人信息,当你真正想做的就是跟踪设备安装。很明显,您正在收集数据。
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • Android ID - 硬件 (可以为 null,可以在出厂重置时更改,可以在有根设备上更改)

    • 由于它可以为'null',我们可以检查'null' 并更改其值,但这意味着它将不再是唯一的。
    • 如果您的用户具有出厂重置设备,则该设备上的值可能已更改或更改,因此如果您正在跟踪用户安装,则可能存在重复条目。
  • WLAN MAC 地址 - 硬件 (需要android.permission.ACCESS_WIFI_STATE

    • 这可能是第二个最佳选择,但您仍在收集和存储直接来自用户的唯一标识符。很明显,您正在收集数据。
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • 蓝牙 MAC 地址 - 硬件 (带蓝牙的设备,需要android.permission.BLUETOOTH

    • 市场上的大多数应用程序都不使用蓝牙,因此如果您的应用程序不使用蓝牙而您包含此功能,则用户可能会产生怀疑。
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • 伪唯一 ID - 软件 (适用于所有 Android 设备)

    • 很可能,可能包含碰撞 - 请参阅下面发布的方法!
    • 这使您可以从用户那里获得 “几乎唯一” 的 ID,而无需使用任何私有 ID。您可以从设备信息创建自己的匿名 ID。

我知道没有任何 “完美” 的方法可以在不使用权限的情况下获取唯一 ID; 但是,有时我们只需要跟踪设备安装。在创建唯一 ID 时,我们可以根据 Android API 提供的信息创建 “伪唯一 ID”,而无需使用额外的权限。通过这种方式,我们可以向用户展示尊重并尝试提供良好的用户体验。

使用伪唯一 ID,您实际上只会遇到基于类似设备这一事实可能存在重复的事实。您可以调整组合方法,使其更加独特; 但是,一些开发人员需要跟踪设备安装,这将基于类似设备执行技巧或性能。

API> = 9:

如果他们的 Android 设备是 API 9 或更高版本,由于 “Build.SERIAL” 字段,这保证是唯一的。

请记住 ,从技术上讲,只有 0.5%的API <9的用户错过了。所以你可以专注于其余的:这是 99.5%的用户!

API <9:

如果用户的 Android 设备低于 API 9; 希望他们没有完成工厂重置,他们的'Secure.ANDROID_ID' 将被保留或不是'null'。 (见http://developer.android.com/about/dashboards/index.html

如果一切都失败了:

如果所有其他方法都失败了,如果用户确实低于 API 9(低于 Gingerbread),重置了他们的设备或'Secure.ANDROID_ID' 返回'null',那么返回的 ID 将完全基于他们的 Android 设备信息。这是碰撞可能发生的地方。

变化:

  • 由于工厂重置而删除'Android.SECURE_ID' 可能会导致值发生变化
  • 编辑代码以更改 API
  • 改变了伪

请看下面的方法:

/**
 * 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 device 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!
    // https://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 device, 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 exception) {
        // String needs to be initialized
        serial = "serial"; // some value
    }

    // Thanks @Joe!
    // https://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();
}

新功能(适用于包含广告和 Google Play 服务的应用):

来自 Google Play Developer 的控制台:

从 2014 年 8 月 1 日开始,Google Play 开发者计划政策要求所有新的应用上传和更新都使用广告 ID 代替任何其他持久性标识符,以用于任何广告目的。学到更多

实施

允许:

<uses-permission android:name="android.permission.INTERNET" />

码:

import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...

// Do not call this function from the main thread. Otherwise, 
// an IllegalStateException will be thrown.
public void getIdThread() {

  Info adInfo = null;
  try {
    adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);

  } catch (IOException exception) {
    // Unrecoverable error connecting to Google Play services (e.g.,
    // the old version of the service doesn't support getting AdvertisingId).

  } catch (GooglePlayServicesAvailabilityException exception) {
    // Encountered a recoverable error connecting to Google Play services. 

  } catch (GooglePlayServicesNotAvailableException exception) {
    // Google Play services is not available entirely.
  }
  final String id = adInfo.getId();
  final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}

来源 / 文档:

http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html

重要:

当 Google Play 服务可用时,广告 ID 旨在完全取代其他标识符的现有使用,以用于广告目的(例如在 Settings.Secure 中使用 ANDROID_ID)。 Google Play 服务不可用的情况由 getAdvertisingIdInfo()引发的 GooglePlayServicesNotAvailableException 指示。

警告,用户可以重置:

http://en.kioskea.net/faq/34732-android-reset-your-advertising-id

我试图引用我从中获取信息的每个链接。如果您遗失并需要加入,请发表评论!

Google Player 服务实例 ID

https://developers.google.com/instance-id/