Android – 是否有唯一的Android设备ID?

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


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

import android.provider.Settings.Secure;

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

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

完全公开:我的应用程序最初使用了以下方法,但不再使用此方法,现在我们使用Emmby答案链接(即生成和保存a )的Android Developer Blog条目中概述的方法。UUID#randomUUID()


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

根据我对设备的测试(所有手机,至少其中一个未启动):

  1. 所有测试的设备都返回了一个值 TelephonyManager.getDeviceId()
  2. 所有GSM设备(全部使用SIM卡进行测试)返回值 TelephonyManager.getSimSerialNumber()
  3. 所有CDMA设备都返回null getSimSerialNumber()(如预期的那样)
  4. 所有添加了Google帐户的设备都会返回一个值 ANDROID_ID
  5. 所有CDMA设备同时用于返回的值相同(或同等价值的推导)ANDROID_IDTelephonyManager.getDeviceId()– 只要一个谷歌帐户设置过程中被添加。
  6. 我还没有机会测试没有SIM卡的GSM设备,没有添加Google帐户的GSM设备,或者任何处于飞行模式的设备。

所以如果你想要一些独特的设备本身,TM.getDeviceId() 应该是足够的。很显然,有些用户比其他用户更偏执,所以散列1个或多个这些标识符可能会很有用,这样字符串对于设备来说仍然是独一无二的,但并不明确标识用户的实际设备。例如,使用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;

最后更新:6/2/15


在阅读关于创建唯一ID,Google开发者博客和Android文档的每个Stack Overflow后,我觉得’Pseudo ID’是最好的选择。

主要问题:硬件vs软件

硬件

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

软件

  • 用户可以擦除/更改他们的ROM,如果他们是根植的
  • 您可以跨平台(iOS,Android,Windows和Web)跟踪用户,
  • 最好希望跟踪个人用户同意是简单地让他们登录(使用OAuth进行无缝连接)

使用Android进行全面细分

– 保证API => 9/10(99.5%的Android设备)的独特性(包括根植设备)

– 没有额外的权限

伪代码:

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发布我们所有的选项(在这个堆栈溢出问题)。

选项清单 – 为什么/为什么不使用它们的原因:

  • 用户电子邮件 – 软件
    • 用户可以更改电子邮件 – 极不可能
    • 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’将被保留或不是’空’。(请参阅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开发者控制台中:

从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/

添加评论

友情链接:蝴蝶教程