43 题: 是否有唯一的Android设备ID?

在...创建的问题 Tue, Jan 30, 2018 12:00 AM

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

    
2552
  1. 如果您使用的是ANDROID_ID,请务必阅读此答案此错误
    2013-01-16 08:16:50Z
  2. 2013-05-24 17:10:51Z
  3. 2014-04-25 05:00:39Z
  4. 我发现这非常有用,因为它讨论了所有可用的唯一标识符,并在每个标识符中加权并得出最佳结论stackoverflow.com/q/27233518/5252270
    2015-08-31 05:58:12Z
  5. 醇>
    30答案                              30 跨度>                         

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

     
    import android.provider.Settings.Secure;
    
    private String android_id = Secure.getString(getContext().getContentResolver(),
                                                            Secure.ANDROID_ID); 
    
        
    1861
    2017-07-24 13:27:27Z
    1. 有时它被称为null,它被记录为“可以在出厂重置时更改”。使用风险自负,可以在root手机上轻松更改。
      2010-06-23 14:21:59Z
    2. 2010-11-27 09:57:21Z
    3. 我认为我们需要注意在第一个答案中使用哈希中的ANDROID_ID,因为它可能在首次运行应用程序时未设置,可能稍后设置,或者可能甚至理论上的变化,因此唯一的ID可能会改变
      2011-02-05 17:06:05Z
    4. 请注意此解决方案存在巨大限制: android-developers.blogspot.com/2011/03 /...
      2011-04-07 20:10:26Z
    5. ANDROID_ID不再唯一标识设备(截至4.2): stackoverflow。 COM /一个/150016分之13465373
      2012-12-15 19:36:42Z
    6. 醇>

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

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


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

    根据我对设备的测试(所有手机,其中至少有一部分未激活):

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

      因此,如果您想要设备本身独有的东西,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

      它适用于我。

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

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

      导入libs

       
      import android.content.Context;
      import android.telephony.TelephonyManager;
      import android.view.View;
      
          
    1105
    2017-12-01 14:37:26Z
    1. 基于电话的ID不会出现在平板设备上,是吗?
      2010-06-23 14:27:54Z
    2. 因此为什么我说大多数都不会一直工作:)我还没有看到这个问题的任何答案对所有设备都是可靠的,所有设备类型,以及所有硬件配置。这就是为什么这个问题从这里开始的原因。很明显,没有最终解决方案。单个设备制造商可能具有设备序列号,但这些设备序列号不会公开供我们使用,并且不是必需的。因此,我们留给了我们可以使用的东西。
      2010-06-29 19:40:29Z
    3. 代码示例效果很好。请记住将<uses-permission android:name="android.permission.READ_PHONE_STATE" />添加到清单文件中。如果存储在数据库中,则返回的字符串长度为36个字符。
      2011-02-27 22:28:48Z
    4. 请注意此解决方案存在巨大限制: android-developers.blogspot.com/2011/03 /...
      2011-04-07 20:09:14Z
    5. @ softarn:我相信你所指的是emmby已经链接到的Android开发人员博客,它解释了你是尝试要说,也许你应该简单地赞成他的评论。无论哪种方式,正如emmby在他的回答中提到的那样,即使博客信息也存在问题。该问题要求提供唯一的 DEVICE 标识符(不是安装标识符),因此我不同意您的陈述。博客假设你想要的东西不一定来跟踪设备,而问题就是要求。我同意该博客。
      2011-07-22 00:45:57Z
    6. 醇>

    上次更新时间:2015年2月2日


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

    主要问题:硬件与软件

    硬件

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

    软件

    • 如果用户已植根
    • ,用户可以擦除/更改他们的ROM
    • 您可以跨平台(iOS,Android,Windows和Web)跟踪用户
    • 跟踪个人用户 同意的最佳方法是让他们登录(使用OAuth使其无缝连接)

    Android的整体细分

    - 保证API的唯一性(包括root设备)&gt; = 9/10(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 the 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&gt; = 9:

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

    记住,从技术上讲,您只有大约0.5%的用户错过谁有API&lt; 9 。所以你可以专注于其余的:这是99.5%的用户!

    API&lt; 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
    • 更改了Pseudo

    请看下面的方法:

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

    重要:

      

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

    警告,用户可以重置:

    http://en.kioskea.net/faq/34732-Android的复位您的广告-ID

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

    Google Player服务实例ID

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

        
    409
    2018-11-27 09:33:00Z
    1. 但是在操作系统更新时Build类不会改变吗?特别是如果API得到更新?如果是这样,你如何保证这是独一无二的? (谈到你写的方法)
      2013-07-13 10:16:16Z
    2. 我在我的应用程序中使用您的方法发送评论。我有坏消息。不幸的是,PsuedoID并不是完全独一无二的。我的服务器为5个ID记录了100多个,对于近30个ID记录了超过30个。最重复的ID是'ffffffff-fc8f-6093-ffff-ffffd8'(159记录)和'ffffffff-fe99-b334-ffff-ffffef'(154次)。也基于时间和评论,显然有不同的人民。到目前为止的总记录是10,000。请让我知道为什么会这样。罐。
      2015-03-17 16:36:19Z
    3. 我在1.5多年前写过这篇文章。我不确定为什么它不是你独一无二的。您可以尝试广告ID。如果没有,您可以提出自己的解决方案。
      2015-03-17 17:35:58Z
    4. sorta ..如果你仔细阅读这个问题,我会非常感激
      2015-04-29 17:41:16Z
    5. @ user1587329谢谢。我试图让每个人保持最新状态。对于硬件与软件和跨平台,这个问题很棘手。
      2015-05-06 14:08:05Z
    6. 醇>

    正如Dave Webb所提到的, Android开发者博客有一篇文章那个小海湾这个。他们首选的解决方案是跟踪应用安装而不是设备,这对大多数用例都有效。博客文章将向您展示完成这项工作所需的代码,我建议您查看它。

    但是,如果您需要设备标识符而不是应用安装标识符,博客文章会继续讨论解决方案。我与Google的某位人士进行了交谈,以便在您需要的情况下对一些项目进行一些额外的澄清。以下是我在上述博客文章中未提及的设备标识符的发现:

    • ANDROID_ID是首选的设备标识符。 ANDROID_ID在Android&lt; = 2.1或&gt; = 2.3版本上完全可靠。只有2.2有帖子中提到的问题。
    • 多个制造商的几个设备受到2.2中的ANDROID_ID错误的影响。
    • 据我所知,所有受影响的设备都有相同的ANDROID_ID 9774d56d682e549c 。这也是模拟器报告的设备ID,btw。
    • 谷歌认为,原始设备制造商已经为许多或大多数设备修补了这个问题,但我能够验证到2011年4月初,至少,找到那些破坏了ANDROID_ID的设备仍然很容易。

    根据Google的建议,我实现了一个类,它将为每个设备生成一个唯一的UUID,在适当的情况下使用ANDROID_ID作为种子,必要时返回TelephonyManager.getDeviceId(),如果失败,则随机使用生成的唯一UUID在应用程序重新启动时保留(但不是应用程序重新安装)。

    请注意,对于必须回退设备ID的设备,唯一ID 在出厂重置期间保持不变。这是值得注意的。如果您需要确保恢复出厂设置将重置您的唯一ID,您可能需要考虑直接回退到随机UUID而不是设备ID。

    同样,此代码用于设备ID,而不是应用安装ID。在大多数情况下,应用程序安装ID可能就是您要查找的内容。但是,如果您确实需要设备ID,则以下代码可能适合您。

     
    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 volatile static 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;
        }
    }
    
        
    327
    2017-09-10 14:22:51Z
    1. 你不应该对各种ID进行哈希处理,以便它们的大小相同吗?此外,您应该对设备ID进行哈希处理,以免意外泄露私人信息。
      2011-04-11 20:10:05Z
    2. 好点,史蒂夫。我更新了代码以始终返回UUID。这确保了a)生成的ID总是相同的大小,并且b)在返回之前对android和设备ID进行哈希处理以避免意外地暴露个人信息。我还更新了说明,注意设备ID将在出厂时重置,并且对于某些用户来说这可能不合适。
      2011-04-11 21:53:08Z
    3. 我相信你是不正确的;首选解决方案是跟踪安装,而不是设备标识符。您的代码比博客文章中的代码长得多且复杂得多,而且我不明白它会增加任何价值。
      2011-04-12 04:58:02Z
    4. 好点,我更新了评论,强烈建议用户使用app安装ID而不是设备ID。但是,我认为这种解决方案对于需要设备而非安装ID的人来说仍然很有价值。
      2011-04-12 12:25:57Z
    5. ANDROID_ID可以在出厂重置时更改,因此无法识别设备
      2011-05-19 08:12:49Z
    6. 醇>

    以下是Reto Meier今年在 Google I /O 演示中使用的代码,以获得独特的代码id for用户:

     
    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;
    }
    

    如果您将其与备份策略相结合,以便将偏好设置发送到云端(在Reto的 talk ,你应该有一个与用户联系的id,并在设备被擦除甚至更换后坚持使用。我打算使用这在今后的分析中(换句话说,我还没有做到这一点:)。

        
    173
    2014-02-09 14:18:21Z
    1. 我使用的是@Lenn Dolling的方法,其中附加了当前时间的唯一ID。但这似乎更简单可靠。感谢Reto Meier和Antony Nolan
      2012-01-03 13:55:27Z
    2. 这很棒但是root设备呢?他们可以访问它并轻松地将uid更改为另一个。
      2012-05-09 21:25:46Z
    3. 如果您在卸载并重新安装后不需要唯一ID,那么这是一个很好的选择(例如,您有三次获胜机会的促销活动/游戏,期间)。
      2012-05-15 21:52:29Z
    4. Meier演示依赖于使用Android备份管理器,而后者依赖于用户选择打开该功能。对于应用程序用户首选项(Meier的使用)来说这很好,因为如果用户没有选择该选项,她就不会获得备份。但是,最初的问题是关于为设备生成唯一ID,并且此ID是按应用生成的,甚至不是每次安装,更不用说每个设备,因为它依赖于用户选择备份选项,其超出用户偏好的用途(例如,限时试用)是有限的。
      2012-12-22 11:15:41Z
    5. 这对卸载或清除数据无效。
      2014-09-02 02:25:43Z
    6. 醇>

    您也可以考虑使用Wi-Fi适配器的MAC地址。如此检索:

     
    WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
    return wm.getConnectionInfo().getMacAddress();
    

    清单中需要权限android.permission.ACCESS_WIFI_STATE

    报告即使未连接Wi-Fi也可用。如果上面的答案中的Joe在他的许多设备上尝试了这个,那就太好了。

    在某些设备上,当Wi-Fi关闭时,它无法使用。

    注意:从Android 6.x开始,它会返回一致的虚假mac地址:02:00:00:00:00:00

        
    100
    2017-04-13 08:52:50Z
    1. 这需要android.permission.ACCESS_WIFI_STATE
      2010-11-01 04:41:54Z
    2. 它们存在吗?我宁愿设想一个无电话设备(AKA平板电脑)...
      2010-12-22 14:23:52Z
    3. 我认为你会发现,当WiFi关闭时,它几乎在所有Android设备上都不可用。关闭WiFi会删除内核级别的设备。
      2012-05-21 23:38:21Z
    4. @ Sanandrea - 让我们面对它,在一个有根设备上,一切都可以被欺骗。
      2013-09-04 09:53:14Z
    5. 在Android M上阻止访问WiFi MAC地址: stackoverflow.com/questions/31329733 /...
      2015-12-22 12:37:39Z
    6. 醇>

    有相当有用的信息 此处

    它涵盖五种不同的ID类型:

    1. IMEI (仅适用于使用电话的Android设备;需要android.permission.READ_PHONE_STATE
    2. 伪唯一ID (适用于所有Android设备)
    3. Android ID (可以为null,可以在出厂重置时更改,可以在root电话上更改)
    4. WLAN MAC地址字符串(需要android.permission.ACCESS_WIFI_STATE
    5. BT MAC地址字符串(带蓝牙的设备,需要android.permission.BLUETOOTH
    82
    2012-02-08 02:16:52Z
    1. 遗漏的重点(此处和文章中):除非打开它们,否则无法获得WLAN或BT MAC!否则我认为WLAN MAC将是完美的标识符。你不能保证用户会打开他们的Wi-Fi,我真的不认为自己打开它是“合适的”。
      2012-10-07 22:44:38Z
    2. @汤姆,你错了。即使关闭WLAN或BT MAC,您仍然可以读取它们。但是,无法保证设备具有WLAN或BT模块。
      2014-08-04 10:20:28Z
    3. 最值得注意的是,本地WiFi和蓝牙MAC地址不再可用.aWifiInfo对象的getMacAddress()方法和BluetoothAdapter.getDefaultAdapter()。getAddress()方法将从现在开始返回02:00:00:00:00:00
      2016-03-30 06:03:59Z
    4. @ sarikakate仅在6.0 Marshmallow及以上版本中才真实...它仍然在6.0以下的棉花糖中正常工作。
      2016-04-13 10:22:39Z
    5. @Smeet是的你是对的。我忘了提到它的工作在6.0以下
      2016-04-14 04:15:18Z
    6. 醇>

    官方Android开发者博客现在有一篇关于这个主题的完整文章, 识别应用安装

        
    48
    2014-02-09 14:00:25Z
    1. 该论点的关键点是,如果你试图从硬件中获取一个唯一的ID,那么你可能犯了一个错误。
      2011-04-12 04:57:05Z
    2. 如果您允许通过恢复出厂设置锁定设备锁定,那么您的试用软件模型就像死了一样好。
      2011-05-04 18:54:16Z
    3. 醇>

    Google I /O 上,Reto Meier发布了一个强有力的解决方案,以解决如何应对大多数开发人员的问题需要跨安装跟踪用户。安东尼诺兰在他的回答中显示了方向,但我认为我会写出完整的方法,以便其他人可以很容易地看到如何做到(我花了一些时间来弄清楚细节)。

    这种方法会为您提供一个匿名的,安全的用户ID,该ID对于不同设备(基于主要Google帐户)和跨安装的用户都是持久的。基本方法是生成随机用户ID并将其存储在应用程序的共享首选项中。然后,您可以使用Google的备用代理存储与云中Google帐户关联的共享偏好设置。

    让我们完整的方法。首先,我们需要创建一个背面使用Android备份服务获取我们的SharedPreferences。首先通过http://developer.android.com/google/backup/signup.html注册您的应用程序。

    Google会为您提供备份服务密钥,您需要将其添加到清单中。您还需要告诉应用程序使用BackupAgent,如下所示:

     
    <application android:label="MyApplication"
             android:backupAgent="MyBackupAgent">
        ...
        <meta-data android:name="com.google.android.backup.api_key"
            android:value="your_backup_service_key" />
    </application>
    

    然后,您需要创建备份代理并告诉它使用帮助代理进行共享优先:

     
    public class MyBackupAgent extends BackupAgentHelper {
        // The name of the SharedPreferences file
        static final String PREFS = "user_preferences";
    
        // A key to uniquely identify the set of backup data
        static final String PREFS_BACKUP_KEY = "prefs";
    
        // Allocate a helper and add it to the backup agent
        @Override
        public void onCreate() {
            SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this,          PREFS);
            addHelper(PREFS_BACKUP_KEY, helper);
        }
    }
    

    要完成备份,您需要在主Activity:

    中创建BackupManager实例  
    BackupManager backupManager = new BackupManager(context);
    

    最后创建一个用户ID(如果它尚不存在),并将其存储在SharedPreferences中:

     
      public static String getUserID(Context context) {
                private static String uniqueID = null;
            private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
        if (uniqueID == null) {
            SharedPreferences sharedPrefs = context.getSharedPreferences(
                    MyBackupAgent.PREFS, 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();
    
                //backup the changes
                BackupManager mBackupManager = new BackupManager(context);
                mBackupManager.dataChanged();
            }
        }
    
        return uniqueID;
    }
    

    即使用户移动设备,此User_ID现在也会在安装过程中保持不变。

    有关此方法的详细信息,请参阅 Reto的演讲

    有关如何实施备份代理的完整详细信息,请参阅 数据备份 。我特别推荐测试底部的部分,因为备份不会立即发生,所以要测试你必须强制备份。

        
    39
    2014-02-09 14:31:17Z
    1. 当用户拥有多个设备时,这是否会导致多个具有相同ID的设备?例如平板电脑和手机。
      2013-03-13 02:25:41Z
    2. 此需要最低目标8。
      2013-04-03 17:24:10Z
    3. 这是在进行应用内购买时创建验证有效负载的首选方式吗?从应用内结算示例代码注释:“因此,良好的开发人员有效负载具有以下特征:1。如果两个不同的用户购买项目,则有效负载在它们之间是不同的,因此一个用户的购买无法重播给另一个用户。 2.有效负载必须能够验证它,即使应用程序不是发起购买流程的人(以便用户在一台设备上购买的项目在用户拥有的其他设备上工作)。“
      2013-08-14 07:02:06Z
    4. @Tosa我有同样的问题。但我们不能再使用同样的技术来创建虚拟设备ID而不是以同样的方式支持它吗?设备ID不会持续擦除或重新安装,但如果我们有持久的用户ID,我们可能不需要设备ID。
      2016-05-19 07:03:33Z
    5. rooted用户可以轻松编辑共享首选项
      2017-11-24 13:29:55Z
    6. 醇>

    我认为这确定了为唯一ID构建骨架的方法......检查一下。

    Pseudo-Unique ID,适用于所有Android设备 某些设备没有手机(例如平板电脑)或出于某种原因,您不希望包含READ_PHONE_STATE权限。您仍然可以读取ROM版本,制造商名称,CPU类型和其他硬件详细信息等详细信息,如果您要将ID用于串行密钥检查或其他一般用途,则非常适合。以这种方式计算的ID不是唯一的:可以找到两个具有相同ID的设备(基于相同的硬件和ROM映像),但实际应用程序中的更改可以忽略不计。为此,您可以使用Build类:

     
    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
    

    大多数Build成员都是字符串,我们在这里做的是获取它们的长度并通过模数转换它。我们有13个这样的数字,我们在前面添加两个(35)以具有与IMEI(15位数)相同的大小ID。这里有其他可能性很好,只需看看这些字符串。 返回类似于355715565309247的内容。不需要特别许可,这种方法非常方便。


    (额外信息:上面给出的技术是从 Pocket Magic 上的文章中复制而来的。 )

        
    35
    2018-06-22 17:30:31Z
    1. 有趣的解决方案。听起来这是一种情况,你真的应该只是哈希所有数据连接而不是试图想出你自己的“哈希”函数。在许多情况下,即使每个值都有不同的实质数据,您也会遇到冲突。我的建议:使用哈希函数,然后将二进制结果转换为十进制,并根据需要截断它。要做得对,尽管你应该使用UUID或完整的哈希字符串。
      2011-04-12 16:28:04Z
    2. 你应该赞扬你的消息来源......这已经直接取消了以下文章: pocketmagic.net/?p=1662
      2011-05-16 12:07:44Z
    3. 这个ID对你不知道的冲突是开放的。它实际上保证在同一载体上的相同设备上是相同的。
      2011-05-26 20:21:30Z
    4. 如果设备升级,这也可能会改变。
      2012-01-25 12:25:54Z
    5. 非常非常糟糕的解决方案。测试了两个Nexus 5 ...返回相同的数字。
      2015-03-04 16:53:23Z
    6. 醇>

    以下代码使用隐藏的Android API返回设备序列号。但是,此代码不适用于三星Galaxy Tab,因为此设备上未设置“ro.serialno”。

     
    String serial = null;
    
    try {
        Class<?> c = Class.forName("android.os.SystemProperties");
        Method get = c.getMethod("get", String.class);
        serial = (String) get.invoke(c, "ro.serialno");
    }
    catch (Exception ignored) {
    
    }
    
        
    34
    2014-02-09 20:08:31Z
    1. 我刚刚在xda开发人员看到ro.serialno用于生成Settings.Secure.ANDROID_ID。所以它们基本上是相同值的不同表示。
      2011-06-23 06:31:59Z
    2. @ Martin:但重置设备时序列号可能不会改变。不是吗?只是ANDROID_ID的新值来自它。
      2011-09-17 08:28:31Z
    3. 实际上,在我测试过的所有设备上,它们完全相同。或者至少哈希值相同(出于隐私原因,我不会将真值写入日志文件)。
      2011-09-18 11:53:58Z
    4. 此值与android.os.Build.SERIAL相同
      2017-03-08 10:25:12Z
    5. android.os.Build.SERIAL将在Android O中弃用,请参阅 android-developers.googleblog.com/2017/04/...
      2017-04-12 08:28:00Z
    6. 醇>

    使用以下代码,您可以将Android OS设备的唯一设备ID作为字符串。

     
    deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
    
        
    22
    2014-02-09 14:10:42Z

    Serial 字段已添加到Build课程中API级别9(Android 2.3 - Gingerbread)。文档说它代表硬件序列号。因此它应该是uniq即,如果它存在于设备上。

    我不知道API级别&gt; = 9的所有设备是否实际支持(= not null)。

        
    18
    2014-02-09 20:09:54Z
    1. 不幸的是,它是“未知”。
      2013-04-15 12:33:03Z
    2. 醇>

    我要补充一点 - 我有一个独特的情况。

    使用:

     
    deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
    

    事实证明,即使我的Viewsonic G平板电脑报告的DeviceID不是Null,每个G平板电脑都会报告相同的数字。

    使用“Pocket Empires”让它变得有趣,它可让您根据“唯一的”DeviceID即时访问某人的帐户。

    我的设备没有手机广播。

        
    15
    2011-03-21 20:46:43Z
    1. 什么是id?是经验9774d56d682e549c
      2013-09-18 13:44:09Z
    2. 哇,很久以前我早就扔了那台平板电脑。不能说。
      2013-09-25 19:36:15Z
    3. 醇>

    有关如何为安装应用程序的每个Android设备获取唯一标识符的详细说明,请参阅官方Android开发人员博客发布 识别应用程序安装

    最好的方法是在安装时自己生成一个,然后在重新启动应用程序时读取它。

    我个人认为这是可以接受但不理想的。 Android提供的任何一个标识符都不适用于所有情况,因为大多数都取决于手机的无线电状态(Wi-Fi开/关,手机开/关,蓝牙开/关)。其他如Settings.Secure.ANDROID_ID必须由制造商实施,并不保证是唯一的。

    以下是将数据写入安装文件的示例,该文件将与应用程序在本地保存的任何其他数据一起存储。

     
    public class Installation {
        private static String sID = null;
        private static final String INSTALLATION = "INSTALLATION";
    
        public synchronized static String id(Context context) {
            if (sID == null) {
                File installation = new File(context.getFilesDir(), INSTALLATION);
                try {
                    if (!installation.exists())
                        writeInstallationFile(installation);
                    sID = readInstallationFile(installation);
                } 
                catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            return sID;
        }
    
        private static String readInstallationFile(File installation) throws IOException {
            RandomAccessFile f = new RandomAccessFile(installation, "r");
            byte[] bytes = new byte[(int) f.length()];
            f.readFully(bytes);
            f.close();
            return new String(bytes);
        }
    
        private static void writeInstallationFile(File installation) throws IOException {
            FileOutputStream out = new FileOutputStream(installation);
            String id = UUID.randomUUID().toString();
            out.write(id.getBytes());
            out.close();
        }
    }
    
        
    14
    2014-02-09 14:15:45Z
    1. 如果你想跟踪应用程序安装,这是完美的。虽然跟踪设备比较棘手,但似乎没有一个完全不透气的解决方案。
      2011-10-03 19:56:53Z
    2. 根设备怎么样?他们可以轻松更改此安装ID,对吧?
      2012-05-09 21:24:47Z
    3. 绝对。 Root可以更改安装ID。您可以使用以下代码块检查root: stackoverflow.com/questions/1101380 /...
      2012-05-18 16:52:34Z
    4. 如果我们重置工厂,文件会被删除吗?
      2015-01-08 10:26:01Z
    5. 如果出厂重置并删除或格式化/data分区,则UUID不同。
      2015-01-12 02:08:37Z
    6. 醇>

    在类文件中添加以下代码:

     
    final TelephonyManager tm = (TelephonyManager) getBaseContext()
                .getSystemService(SplashActivity.TELEPHONY_SERVICE);
        final String tmDevice, tmSerial, androidId;
        tmDevice = "" + tm.getDeviceId();
        Log.v("DeviceIMEI", "" + tmDevice);
        tmSerial = "" + tm.getSimSerialNumber();
        Log.v("GSM devices Serial Number[simcard] ", "" + tmSerial);
        androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(),
                android.provider.Settings.Secure.ANDROID_ID);
        Log.v("androidId CDMA devices", "" + androidId);
        UUID deviceUuid = new UUID(androidId.hashCode(),
                ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
        String deviceId = deviceUuid.toString();
        Log.v("deviceIdUUID universally unique identifier", "" + deviceId);
        String deviceModelName = android.os.Build.MODEL;
        Log.v("Model Name", "" + deviceModelName);
        String deviceUSER = android.os.Build.USER;
        Log.v("Name USER", "" + deviceUSER);
        String devicePRODUCT = android.os.Build.PRODUCT;
        Log.v("PRODUCT", "" + devicePRODUCT);
        String deviceHARDWARE = android.os.Build.HARDWARE;
        Log.v("HARDWARE", "" + deviceHARDWARE);
        String deviceBRAND = android.os.Build.BRAND;
        Log.v("BRAND", "" + deviceBRAND);
        String myVersion = android.os.Build.VERSION.RELEASE;
        Log.v("VERSION.RELEASE", "" + myVersion);
        int sdkVersion = android.os.Build.VERSION.SDK_INT;
        Log.v("VERSION.SDK_INT", "" + sdkVersion);
    

    添加AndroidManifest.xml:

     
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    
        
    10
    2015-05-18 04:17:20Z

    使用TelephonyManagerANDROID_ID作为String的Android OS设备的唯一设备ID是通过以下方式获得的:

     
    String deviceId;
    final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    if (mTelephony.getDeviceId() != null) {
        deviceId = mTelephony.getDeviceId();
    }
    else {
        deviceId = Secure.getString(
                       getApplicationContext().getContentResolver(),
                       Secure.ANDROID_ID);
    }
    

    但我强烈推荐Google建议的方法,请参阅 识别应用安装

        
    9
    2015-05-26 04:51:45Z

    有很多不同的方法可以解决这些ANDROID_ID问题(有时可能是null或特定模型的设备总是返回相同的ID),但有利有弊:

    • 实施自定义ID生成算法(基于应该是静态且不会更改的设备属性 - &gt;谁知道)
    • 滥用其他ID,例如 IMEI ,序列号,Wi-Fi /蓝牙MAC地址(它们不会存在于所有设备上,或者需要额外的权限)

    我自己更喜欢使用现有的OpenUDID实现(请参阅 https://github.com/ylechelle/OpenUDID)for Android(请参阅 https://github.com/vieux/OpenUDID )。对于上述问题,它很容易集成并使用ANDROID_ID和后备。

        
    8
    2014-02-09 14:26:06Z

    IMEI 怎么样?这对Android或其他移动设备来说是独一无二的。

        
    7
    2011-07-14 20:01:22Z
    1. 不适用于没有IMEI的平板电脑,因为它们没有连接到我的移动运营商。
      2012-01-05 16:54:50Z
    2. 更不用说有ESN而不是IMEI的CDMA设备。
      2012-01-25 12:25:01Z
    3. @David Given有没有Android的CDMA?
      2012-01-26 14:29:16Z
    4. 它只会这样做电话:)平板电脑可能没有。
      2012-01-26 19:39:46Z
    5. @ ElzoValugi这是“今天”已经且并非所有平板电脑都有SIM卡。
      2012-09-25 09:42:45Z
    6. 醇>

    以下是我如何生成唯一ID:

     
    public static String getDeviceId(Context ctx)
    {
        TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
    
        String tmDevice = tm.getDeviceId();
        String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
        String serial = null;
        if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL;
    
        if(tmDevice != null) return "01" + tmDevice;
        if(androidId != null) return "02" + androidId;
        if(serial != null) return "03" + serial;
        // other alternatives (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)
    
        return null;
    }
    
        
    7
    2013-05-27 18:34:47Z
    1. 如果我们在6.0版本中使用ReadPhoneState请求运行时权限
      2016-11-17 14:06:53Z
    2. 醇>

    我的两分钱 - 注意这是设备(错误)唯一ID - 而不是 Android开发者的博客

    值得注意的是,@ emmby提供的解决方案落后于每个应用程序ID,因为SharedPreferences未在流程(请参阅此处此处)。所以我完全避免了这一点。

    相反,我封装了在枚举中获取(设备)ID的各种策略 - 更改枚举常量的顺序会影响获取ID的各种方式的优先级。返回第一个非null ID或抛出异常(根据不给出null含义的优秀Java实践)。所以例如我首先使用TELEPHONY - 但是一个好的默认选择是ANDROID_ID 的测试强>

     
    import android.Manifest.permission;
    import android.bluetooth.BluetoothAdapter;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.net.wifi.WifiManager;
    import android.provider.Settings.Secure;
    import android.telephony.TelephonyManager;
    import android.util.Log;
    
    // TODO : hash
    public final class DeviceIdentifier {
    
        private DeviceIdentifier() {}
    
        /** @see http://code.google.com/p/android/issues/detail?id=10603 */
        private static final String ANDROID_ID_BUG_MSG = "The device suffers from "
            + "the Android ID bug - its ID is the emulator ID : "
            + IDs.BUGGY_ANDROID_ID;
        private static volatile String uuid; // volatile needed - see EJ item 71
        // need lazy initialization to get a context
    
        /**
         * Returns a unique identifier for this device. The first (in the order the
         * enums constants as defined in the IDs enum) non null identifier is
         * returned or a DeviceIDException is thrown. A DeviceIDException is also
         * thrown if ignoreBuggyAndroidID is false and the device has the Android ID
         * bug
         *
         * @param ctx
         *            an Android constant (to retrieve system services)
         * @param ignoreBuggyAndroidID
         *            if false, on a device with the android ID bug, the buggy
         *            android ID is not returned instead a DeviceIDException is
         *            thrown
         * @return a *device* ID - null is never returned, instead a
         *         DeviceIDException is thrown
         * @throws DeviceIDException
         *             if none of the enum methods manages to return a device ID
         */
        public static String getDeviceIdentifier(Context ctx,
                boolean ignoreBuggyAndroidID) throws DeviceIDException {
            String result = uuid;
            if (result == null) {
                synchronized (DeviceIdentifier.class) {
                    result = uuid;
                    if (result == null) {
                        for (IDs id : IDs.values()) {
                            try {
                                result = uuid = id.getId(ctx);
                            } catch (DeviceIDNotUniqueException e) {
                                if (!ignoreBuggyAndroidID)
                                    throw new DeviceIDException(e);
                            }
                            if (result != null) return result;
                        }
                        throw new DeviceIDException();
                    }
                }
            }
            return result;
        }
    
        private static enum IDs {
            TELEPHONY_ID {
    
                @Override
                String getId(Context ctx) {
                    // TODO : add a SIM based mechanism ? tm.getSimSerialNumber();
                    final TelephonyManager tm = (TelephonyManager) ctx
                            .getSystemService(Context.TELEPHONY_SERVICE);
                    if (tm == null) {
                        w("Telephony Manager not available");
                        return null;
                    }
                    assertPermission(ctx, permission.READ_PHONE_STATE);
                    return tm.getDeviceId();
                }
            },
            ANDROID_ID {
    
                @Override
                String getId(Context ctx) throws DeviceIDException {
                    // no permission needed !
                    final String andoidId = Secure.getString(
                        ctx.getContentResolver(),
                        android.provider.Settings.Secure.ANDROID_ID);
                    if (BUGGY_ANDROID_ID.equals(andoidId)) {
                        e(ANDROID_ID_BUG_MSG);
                        throw new DeviceIDNotUniqueException();
                    }
                    return andoidId;
                }
            },
            WIFI_MAC {
    
                @Override
                String getId(Context ctx) {
                    WifiManager wm = (WifiManager) ctx
                            .getSystemService(Context.WIFI_SERVICE);
                    if (wm == null) {
                        w("Wifi Manager not available");
                        return null;
                    }
                    assertPermission(ctx, permission.ACCESS_WIFI_STATE); // I guess
                    // getMacAddress() has no java doc !!!
                    return wm.getConnectionInfo().getMacAddress();
                }
            },
            BLUETOOTH_MAC {
    
                @Override
                String getId(Context ctx) {
                    BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
                    if (ba == null) {
                        w("Bluetooth Adapter not available");
                        return null;
                    }
                    assertPermission(ctx, permission.BLUETOOTH);
                    return ba.getAddress();
                }
            }
            // TODO PSEUDO_ID
            // http://www.pocketmagic.net/2011/02/android-unique-device-id/
            ;
    
            static final String BUGGY_ANDROID_ID = "9774d56d682e549c";
            private final static String TAG = IDs.class.getSimpleName();
    
            abstract String getId(Context ctx) throws DeviceIDException;
    
            private static void w(String msg) {
                Log.w(TAG, msg);
            }
    
            private static void e(String msg) {
                Log.e(TAG, msg);
            }
        }
    
        private static void assertPermission(Context ctx, String perm) {
            final int checkPermission = ctx.getPackageManager().checkPermission(
                perm, ctx.getPackageName());
            if (checkPermission != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException("Permission " + perm + " is required");
            }
        }
    
        // =========================================================================
        // Exceptions
        // =========================================================================
        public static class DeviceIDException extends Exception {
    
            private static final long serialVersionUID = -8083699995384519417L;
            private static final String NO_ANDROID_ID = "Could not retrieve a "
                + "device ID";
    
            public DeviceIDException(Throwable throwable) {
                super(NO_ANDROID_ID, throwable);
            }
    
            public DeviceIDException(String detailMessage) {
                super(detailMessage);
            }
    
            public DeviceIDException() {
                super(NO_ANDROID_ID);
            }
        }
    
        public static final class DeviceIDNotUniqueException extends
                DeviceIDException {
    
            private static final long serialVersionUID = -8940090896069484955L;
    
            public DeviceIDNotUniqueException() {
                super(ANDROID_ID_BUG_MSG);
            }
        }
    }
    
        
    7
    2017-05-23 12:26:36Z

    这里有30多个答案,有些是相同的,有些是独一无二的。这个答案基于这些答案中的一小部分。其中一个是@Lenn Dolling的答案。

    它结合了3个ID并创建一个32位十六进制字符串。它对我来说非常有效。

    3个ID是:
    Pseudo-ID - 它是根据物理设备规格生成的
    ANDROID_ID - Settings.Secure.ANDROID_ID
    蓝牙地址 - 蓝牙适配器地址

    它将返回如下内容: 551F27C060712A72730B0A0F734064B1

    注意:您始终可以向longId字符串添加更多ID。例如,Serial#。 wifi适配器地址。 IMEI。通过这种方式,您可以使每台设备更加独特。

     
    @SuppressWarnings("deprecation")
    @SuppressLint("HardwareIds")
    public static String generateDeviceIdentifier(Context context) {
    
            String pseudoId = "35" +
                    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;
    
            String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
    
            BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            String btId = "";
    
            if (bluetoothAdapter != null) {
                btId = bluetoothAdapter.getAddress();
            }
    
            String longId = pseudoId + androidId + btId;
    
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("MD5");
                messageDigest.update(longId.getBytes(), 0, longId.length());
    
                // get md5 bytes
                byte md5Bytes[] = messageDigest.digest();
    
                // creating a hex string
                String identifier = "";
    
                for (byte md5Byte : md5Bytes) {
                    int b = (0xFF & md5Byte);
    
                    // if it is a single digit, make sure it have 0 in front (proper padding)
                    if (b <= 0xF) {
                        identifier += "0";
                    }
    
                    // add number to string
                    identifier += Integer.toHexString(b);
                }
    
                // hex string to uppercase
                identifier = identifier.toUpperCase();
                return identifier;
            } catch (Exception e) {
                Log.e("TAG", e.toString());
            }
            return "";
    }
    
        
    7
    2017-03-08 14:01:34Z
    1. UUID 添加到longId并将其存储在一个文件中,将使其成为最独特的标识符:String uuid = UUID.randomUUID().toString();
      2017-04-11 07:08:32Z
    2. 如果所有其他方法都失败了,如果用户确实低于API 9(低于Gingerbread),则重置手机或“Secure.ANDROID_ID”。如果返回'null',那么返回的ID将完全基于他们的Android设备信息。这是碰撞可能发生的地方。尽量不要使用DISPLAY,HOST或ID - 这些项目可能会改变。如果存在冲突,则会有重叠数据。来源: gist.github.com/pedja1/fe69e8a80ed505500caa
      2017-04-11 07:17:53Z
    3. 如果我们尝试通过这行代码获取唯一编号,我们可以说它是唯一ID并且它永远不会与任何其他设备冲突吗?
      2019-04-03 06:06:46Z
    4. @ Ninja由于BLE mac地址是唯一的,因此生成的ID将始终是唯一的。但是,如果你真的想确定,我会建议在longId上添加一个UUID。改变这样一行:String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();这保证生成的ID将是唯一的。
      2019-04-03 12:38:35Z
    5. @ᴛʜᴇᴘᴀᴛᴇʟ非常感谢你们这个巨大的帮助。实际上我的应用程序非常敏感的数据,所以我需要确定它,这就是为什么我只是确认这些事情。
      2019-04-05 05:32:32Z
    6. 醇>

    另一种方法是在没有任何权限的情况下在应用中使用/sys/class/android_usb/android0/iSerial

     
    user@creep:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
    -rw-r--r-- root     root         4096 2013-01-10 21:08 iSerial
    user@creep:~$ adb shell cat /sys/class/android_usb/android0/iSerial
    0A3CXXXXXXXXXX5
    

    要在Java中执行此操作,只需使用FileInputStream打开iSerial文件并读出字符即可。请确保将其包装在异常处理程序中,因为并非所有设备都具有此文件。

    至少已知以下设备具有此文件可读性:

    • Galaxy Nexus
    • Nexus S
    • 摩托罗拉Xoom 3G
    • 东芝AT300
    • HTC One V
    • Mini MK802
    • 三星Galaxy S II

    您还可以看到我的博客 将Android硬件序列号泄露给非特权应用 我将讨论其他可用于获取信息的文件。

        
    6
    2014-02-09 14:41:44Z
    1. 我刚刚阅读了你的博文。我相信这不是唯一的:Build.SERIAL也可以没有任何权限,并且(理论上)是一个唯一的硬件序列号。
      2013-04-30 21:14:27Z
    2. 你是对的。这只是跟踪设备的另一种方式,正如您所说,这两种方式都不需要应用程序权限。
      2014-03-14 17:16:51Z
    3. 醇>

    对于特定Android设备的硬件识别,您可以检查MAC地址。

    你可以这样做: AndroidManifest.xml中的

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

    现在在您的代码中:

     
    List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());
    
    for (NetworkInterface interface : interfacesList) {
       // This will give you the interface MAC ADDRESS
       interface.getHardwareAddress();
    }
    

    在每个Android设备中,他们至少是一个“wlan0”接口巫婆是WI-FI芯片。 即使没有打开WI-FI,此代码也能正常工作。

    P.S。 它们是从包含MACS的列表中获得的一堆其他接口但是这可以在不同的电话之间进行更改。

        
    6
    2015-11-09 05:19:39Z

    我使用以下代码获取IMEI或使用安全。 ANDROID_ID作为替代方案,当设备没有电话功能时:

     
    String identifier = null;
    TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
    if (tm != null)
          identifier = tm.getDeviceId();
    if (identifier == null || identifier .length() == 0)
          identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);
    
        
    6
    2016-07-20 03:21:09Z

    更具体地说,Settings.Secure.ANDROID_ID。这是在设备首次启动时生成并存储的64位数量。擦除设备时会重置。

    ANDROID_ID似乎是唯一设备标识符的不错选择。有一些缺点:首先,它在2.2 (“Froyo”).之前的Android版本上并非100%可靠。此外,在一个主要制造商的流行手机中至少有一个被广泛观察到的错误,其中每个实例都具有相同的ANDROID_ID。 /p>     

    6
    2016-07-20 03:21:40Z
    1. 这个答案是旧谷歌博客的副本 android-developers.googleblog.com/2011/03 /... 。所以bug已经解决了?
      2017-01-27 11:22:56Z
    2. 醇>

    TelephonyManger.getDeviceId()返回唯一的设备ID,例如GSM的IMEI和CDMA手机的MEID或ESN。

     
    final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);            
    String myAndroidDeviceId = mTelephony.getDeviceId(); 
    

    但我建议使用:

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

     
        String   myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
    

    有时 TelephonyManger.getDeviceId()将返回null,因此为了确保您将使用此方法的唯一ID:

     
    public String getUniqueID(){    
        String myAndroidDeviceId = "";
        TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        if (mTelephony.getDeviceId() != null){
            myAndroidDeviceId = mTelephony.getDeviceId(); 
        }else{
             myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
        }
        return myAndroidDeviceId;
    }
    
        
    5
    2015-02-06 15:00:25Z
    1. 我最近发现一个客户端类型为SM-G928F /Galaxy S6 edge +的设备只能为Android提供15个而不是16个十六进制数字ID。
      2016-03-13 16:16:34Z
    2. 醇>

    Google实例ID

    2015年I /O发布;在Android上需要播放服务7.5。

    https://developers.google.com/instance-id/
    https://developers.google.com/instance-id/guides/android - 实施

     
    InstanceID iid = InstanceID.getInstance( context );   // Google docs are wrong - this requires context
    String id = iid.getId();  // blocking call
    

    Google似乎打算将此ID用于识别Android,Chrome和iOS上的安装。

    它识别安装而不是设备,但是再次,ANDROID_ID(这是接受的答案)现在不再识别设备。使用ARC运行时,每次安装都会生成一个新的ANDROID_ID(详细信息),就像这个新的实例ID一样。此外,我认为识别安装(而不是设备)是我们大多数人真正想要的。

    实例ID的优势

    在我看来,谷歌打算将它用于此目的(识别您的安装),它是跨平台的,并且可用于许多其他目的(请参阅上面的链接)。

    如果您使用GCM,那么您最终需要使用此实例ID,因为您需要它才能获取GCM令牌(它取代旧的GCM注册ID)。

    缺点/问题

    在当前实施(GPS 7.5)中,当您的应用请求时,会从服务器检索实例ID。这意味着上面的调用是阻塞调用 - 在我不科学的测试中,如果设备在线则需要1-3秒,如果离线需要0.5-1.0秒(可能这是在放弃和生成之前等待的时间长度随机ID)。这是在北美使用Android 5.1.1和GPS 7.5在Nexus 5上测试的。

    如果您将ID用于他们想要的目的 - 例如。应用认证,应用识别,GCM - 我认为这1-3秒可能会令人讨厌(当然,取决于您的应用)。

        
    5
    2017-05-23 10:31:37Z
    1. instanceID的另一个重要缺点是,如果用户清除应用程序的数据,将为您生成一个新的instanceID。
      2015-07-08 16:13:31Z
    2. 有趣,但我认为它并没有真正改变潜在的用例:实例ID,如android_id,不适合识别设备。因此,您的服务器将看到用户清除数据,如用户卸载和重新安装您的应用程序 - 这不是不合理的。
      2015-07-08 20:11:53Z
    3. 醇>

    Android设备mac id也是唯一的ID,如果我们格式化设备本身就不会改变,所以使用以下代码来获取mac id

     
    WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    WifiInfo info = manager.getConnectionInfo();
    String address = info.getMacAddress();
    

    另外,请不要忘记将相应的权限添加到您的帐户中 的AndroidManifest.xml

     
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    
        
    5
    2016-10-03 11:02:10Z
    1. 不幸的是,如果没有当前的WiFi连接,这将不起作用。来自文档(重点已添加):“返回有关当前Wi-Fi连接的动态信息,如果有任何活动。”
      2016-10-05 00:32:14Z
    2. 同样通过在设备上授予root访问权限,可以欺骗mac地址
      2017-07-03 16:33:35Z
    3. 醇>

    Google现在有广告ID
    这也可以使用,但请注意:

      

    广告ID是用户特定的,唯一的,可重置的ID

      

    可让用户重置其标识符或退出Google Play应用中的针对用户兴趣的广告。

    所以尽管这个id可能会改变,但似乎很快我们可能没有选择权,取决于这个id。

    更多信息@ develper.android

    此处复制粘贴代码

    HTH

        
    4
    2013-11-02 13:23:34Z
    1. 可以重置:广告ID是一个唯一但用户可重置的字符串标识符,可让广告网络和其他应用匿名识别用户。
      2013-11-06 14:37:40Z
    2. (@ JaredBurrows yeap,在帖子中提到...)
      2013-11-06 17:04:00Z
    3. 醇>
来源放置 这里