安卓设备的Secure.ANDROID_ID是唯一的吗?

101

在查找相关ID并看到有关它的线程后,我确定了。起初我认为设备可能会返回null,并且我已经将其作为默认值放置在某个位置。但事实并非如此。我确定在多个设备上获取相同的值。 - FoamyGuy
1
我找到了完美的答案:https://dev59.com/ymQo5IYBdhLWcg3wZusc#16929647 - Pratik Butani
对于不唯一的情况,请使用此库,该库带有Identity.getDeviceId(context) - caw
7个回答

59

请查看此线程, 但是要小心,因为它被记录为“可以在出厂设置重置时更改”。风险自负,并且可以在已root的手机上轻松更改。此外,似乎一些制造商的手机存在重复编号的问题,线程。根据您的需求,我可能不会将其用作唯一标识符。


22
很遗憾,ANDROID_ID的实现方式如此不尽人意!根据谷歌的文档,其初衷是生成一个更持久的ID: "一个64位数字(作为十六进制字符串),在设备首次启动时随机生成,应该在设备寿命内保持不变"。 - Someone Somewhere
4
根据这个帖子链接,"此外,某个主要制造商的流行手机中至少有一个广为人知的错误,每个实例具有相同的ANDROID_ID。"最好不要使用它。 - Muhammad Riyaz
5
那是在2011年Froyo期间的事情,现在已经不相关了。 - Aubtin Samai
设备备份/恢复或设备克隆怎么样?有没有关于具有相同ANDROID_ID的机会的想法?当然,假设设备没有root。 - FunkSoulBrother

50

Android O将改变ANDROID_ID的行为。在手机上,ANDROID_ID将根据每个用户和每个应用程序而异。

引用自:https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html

Android ID

在Android O中,Android ID(Settings.Secure.ANDROID_ID或SSAID)对于设备上每个应用程序和每个用户具有不同的值。需要设备范围标识符的开发人员应该使用可重置的标识符,例如广告标识符(Advertising ID),从而赋予用户更多的控制权。广告标识符还提供了面向用户的设置,以限制广告跟踪。

此外,在Android O中:

  • 只要包名称和签名密钥相同,ANDROID_ID值在卸载/重新安装软件包时不会更改。应用程序可以依赖此值来在重新安装后保持状态。
  • 如果应用程序安装在早期版本的Android设备上,则在将设备更新为Android O时,Android ID仍然保持不变,除非卸载并重新安装该应用程序。
  • Android ID的值仅在设备恢复出厂设置或签名密钥在卸载和重新安装事件之间轮换时才会更改。
  • 仅对于预装Google Play服务和广告标识符的设备制造商需要进行此更改。其他设备制造商可以提供可重置的替代标识符或继续提供ANDROID ID。

1
谢谢。我一直在拔头发,试图弄清楚为什么在我的妻子的手机上使用Device ID报告的(md5加密的)ID时,我没有收到测试广告,然后为什么这个值与通过adb shell settings命令报告的值不匹配...我以为我疯了。 - ecv

24

存在多种解决方案,但没有一种是完美的。我们逐一来看。

1. 唯一电话号码 (IMEI, MEID, ESN, IMSI)

  • 此解决方案需要向用户请求android.permission.READ_PHONE_STATE权限,很难在您制作的应用程序类型之后进行合理的解释。

  • 此外,此解决方案仅限于智能手机,因为平板电脑不具备电话服务。一个优点是该值在设备出厂重置时保留。

2. MAC地址

  • 您还可以尝试从配备Wi-Fi或蓝牙硬件的设备中获取MAC地址。但是,不建议使用此解决方案,因为并非所有设备都具有Wi-Fi连接。即使用户拥有Wi-Fi连接,也必须打开才能检索数据。否则,调用将不报告MAC地址。

3. 序列号

  • 没有电话服务的设备(如平板电脑)必须报告可通过android.os.Build.SERIAL获得的唯一设备ID,自Android 2.3 Gingerbread以来已可用。一些具有电话服务的手机也可以定义序列号。由于并非所有Android设备都具有序列号,因此此解决方案不可靠。

4. 安全的Android ID

  • 在设备首次启动时,会生成并存储一个随机值。此值可通过Settings.Secure.ANDROID_ID获得。这是一个64位数字,应在设备的整个生命周期内保持不变。ANDROID_ID似乎是唯一设备标识符的良好选择,因为它适用于智能手机和平板电脑。

String androidId = Settings.Secure.getString(getContentResolver(),Settings.Secure.ANDROID_ID);
  • 然而,如果对设备进行恢复出厂设置,则该值可能会更改。此外,有一个已知的问题,来自某一制造商的受欢迎的手机具有相同的ANDROID_ID。显然,解决方案并不是完全可靠的。

  • 5.使用UUID

    • 由于大多数应用程序的要求是识别特定安装而不是物理设备,因此一个好的解决方案是使用UUID类来为用户获取唯一标识。以下解决方案是由Google在Google I/O演示中由Reto Meier提供的:

    private static String uniqueID = null;
    private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
    public synchronized static String id(Context context) {
       if (uniqueID == null) {
           SharedPreferences sharedPrefs = context.getSharedPreferences(
                   PREF_UNIQUE_ID, Context.MODE_PRIVATE);
           uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
           if (uniqueID == null) {
               uniqueID = UUID.randomUUID().toString();
               Editor editor = sharedPrefs.edit();
               editor.putString(PREF_UNIQUE_ID, uniqueID);
               editor.commit();
           }
       }    return uniqueID;
    }
    

    在Android上识别特定设备并不容易。有许多很好的理由可以避免这样做。最好的解决方案可能是使用UUID解决方案来识别特定安装。 credit: 博客


    请问您能告诉我哪款热门手机在每个实例上都具有相同的ANDROID_ID吗?我找到的唯一参考资料(不断重申)是来自2011年。 - Viktor Brešan

    16

    因此,如果您想要与设备本身唯一相关的内容,TM.getDeviceId() 应该就足够了。

    以下是获取电信管理器 ID 的代码。您正在使用的 Android 设备 ID 可能会在出厂设置中更改,并且某些制造商在提供唯一 ID 方面存在问题。

    TelephonyManager tm = 
            (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
    String androidId = Secure.getString(this.getContentResolver(), Secure.ANDROID_ID);
    Log.d("ID", "Android ID: " + androidId);
    Log.d("ID", "Device ID : " + tm.getDeviceId());
    

    请确保使用以下命令获取TelephonyManager的权限:

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

    3
    是的,使用getDeviceId()也可以。但要非常小心,因为它可能返回null。(在没有3G/手机调制解调器的设备上) 我使用的一种解决方案是回退到Wifi的mac地址(当然,这也可能会丢失,但通常不会在同一台设备上)。 - vdstw
    21
    需要READ_PHONE_STATE权限是很糟糕的。请使用ANDROID_ID代替。 - dolmen
    是的,在没有3G的设备上(市场上有很多这样的设备),它可能会返回null,那么你应该使用WiFi——在设备重启后至少开/关一次之前,其GUID也可能为null。如果没有WiFi……真是个噩梦,在Windows CE上,每个设备都有一个GUID,生活是美好的。 - marcinj
    经过成千上万次的请求后,发现设备ID(即IMEI)可以在假设备上重复,因此不是一个完全可靠的解决方案。我将回归使用getSimSerialNumber和ANDROID_ID作为备用方案。 - Ajibola
    1
    Android 10(API级别29)增加了对不可重置标识符的限制,包括IMEI和序列号。在实施TM.getDeviceId()方法之前,请先查看Android >=10设备上设备标识符的新权限要求 - Ezekiel Sebastine

    6

    我已经阅读了一些关于此事的内容,不幸的是不能依靠ANDROID_ID来唯一标识一个设备。

    在Android合规要求中似乎没有强制执行它,因此制造商似乎会按照自己的方式实现它,包括一些将其更多地用作“型号”ID等。

    此外,请注意,即使制造商编写了生成器将其变成UUID(例如),也不能保证经过工厂重置后仍然存在。


    6
    我认为这是设计上的翻译保证,即经过出厂设置后不会存活,这在文档中有所提及。而这正是实现它的正确方式。 - Violet Giraffe
    那么我们如何生成 GUID? - IgorGanapolsky

    1

    在这里列出一个备选方案,即广告 ID:

    https://support.google.com/googleplay/android-developer/answer/6048248?hl=en

    从上面的链接复制:
    广告 ID 是由 Google Play 服务提供的独特、可重置的广告标识符,它为用户提供更好的控制,并为开发人员提供一个简单、标准的系统来继续赚取他们的应用程序。它使用户能够在 Google Play 应用中重置其标识符或选择退出个性化广告(以前称为基于兴趣的广告)。
    限制如下:
    1.仅支持 Google Play 启用的设备。 2.隐私政策:https://support.google.com/googleplay/android-developer/answer/113469?hl=en&rd=1#privacy

    这个能在没有Google Play(服务)的手机上运行吗? - Max Waterman
    这不是一个GUID。 - IgorGanapolsky

    0
    //Fields
    String myID;
    int myversion = 0;
    
    
    myversion = Integer.valueOf(android.os.Build.VERSION.SDK);
    if (myversion < 23) {
            TelephonyManager mngr = (TelephonyManager) 
    getSystemService(Context.TELEPHONY_SERVICE);
            myID= mngr.getDeviceId();
        }
        else
        {
            myID = 
    Settings.Secure.getString(getApplicationContext().getContentResolver(), 
    Settings.Secure.ANDROID_ID);
        }
    

    是的,Secure.ANDROID_ID对于每个设备都是唯一的。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接