是否有唯一的Android设备ID?

时间:2010-05-07 00:47:28

标签: android uniqueidentifier

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

52 个答案:

答案 0 :(得分:1842)

Settings.Secure#ANDROID_ID将Android ID作为 unique for each user 64位十六进制字符串返回。

import android.provider.Settings.Secure;

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

答案 1 :(得分:1099)

更新:从Android的最新版本开始,ANDROID_ID的许多问题都已得到解决,我相信这种方法已不再需要。请查看Anthony's answer

完全披露:我的应用最初使用了以下方法但不再使用此方法,现在我们使用Android Developer Blog条目中列出的方法emmby's answer链接到(即生成并保存UUID#randomUUID())。


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

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

  1. 所有测试的设备都返回了TelephonyManager.getDeviceId()
  2. 的值
  3. 所有GSM设备(均使用SIM测试)返回TelephonyManager.getSimSerialNumber()
  4. 的值
  5. getSimSerialNumber()(正如预期的那样)所有CDMA设备都返回null
  6. 添加了Google帐户的所有设备都返回了ANDROID_ID
  7. 的值
  8. 只要在期间添加了Google帐户,所有CDMA设备都会为ANDROID_IDTelephonyManager.getDeviceId() - 返回相同的值(或相同值的推导值)设置。
  9. 我还没有机会测试没有SIM卡的GSM设备,没有添加Google帐户的GSM设备,或者飞机模式下的任何设备。
  10. 因此,如果您想要设备本身的独特内容,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;
    

答案 2 :(得分:404)

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


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

主要问题:硬件与软件

硬件

  • 用户可以更改他们的硬件,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发布 all of our options (在此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" />How to get the Android device's primary e-mail address
  • 用户电话号码 - 软件

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

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

    • 因为它可以是“空”,我们可以检查“空”&#39;并改变它的价值,但这意味着它将不再是唯一的。
    • 如果您的用户具有出厂重置设备,则该值可能已在已植根设备上更改或更改,因此如果您要跟踪用户安装,则可能存在重复条目。
  • 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设备)

    • 很可能,可能包含碰撞 - 请参阅下面发布的方法!
    • 这使您可以拥有一个几乎独一无二的&#39;来自用户的ID,不带任何私密内容。您可以从设备信息创建自己的匿名ID。

我知道没有任何完美的&#39;不使用权限获取唯一ID的方法;但是,有时我们只需要跟踪设备安装。在创建唯一ID时,我们可以创建一个伪唯一身份ID&#39;仅基于Android API为我们提供的信息而不使用额外权限。通过这种方式,我们可以向用户展示尊重并尝试提供良好的用户体验。

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

API&gt; = 9:

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

记住,您在技术上只错过了大约0.5%的用户who have API < 9。所以你可以专注于其余的:这是99.5%的用户!

API&lt; 9:

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

如果一切都失败了:

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

的变化:

  • 删除了&#39; Android.SECURE_ID&#39;因工厂重置可能会导致价值变化
  • 编辑代码以更改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-reset-your-advertising-id

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

Google Player服务实例ID

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

答案 3 :(得分:325)

正如Dave Webb所提到的,Android Developer Blog has an article涵盖了这一点。他们首选的解决方案是跟踪应用安装而不是设备,这对大多数用例都有效。博客文章将向您展示完成这项工作所需的代码,我建议您查看它。

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

  • ANDROID_ID是首选的设备标识符。 ANDROID_ID在Android&lt; = 2.1或&gt; = 2.3版本上完全可靠。只有2.2有帖子中提到的问题。
  • 多个制造商的几个设备受到2.2中的ANDROID_ID错误的影响。
  • 据我所知,所有受影响的设备都有the same 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;
    }
}

答案 4 :(得分:171)

以下是Reto Meier今年在Google I/O演示文稿中使用的代码,以获取用户的唯一ID:

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,并在设备被擦除后立即停留,甚至我计划在今后的分析中使用它(换句话说,我还没有那么做过)。

答案 5 :(得分:100)

您也可以考虑使用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

答案 6 :(得分:82)

有相当有用的信息 here

它涵盖五种不同的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

答案 7 :(得分:47)

官方Android开发者博客现在有一篇关于这个主题的完整文章, Identifying App Installations

答案 8 :(得分:39)

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's talk

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

答案 9 :(得分:35)

我认为这确定了为唯一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上的文章中复制而来的。)

答案 10 :(得分:34)

以下代码使用隐藏的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) {

}

答案 11 :(得分:22)

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

deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 

答案 12 :(得分:18)

在API级别9(Android 2.3 - Gingerbread)中的Build类中添加了Serial字段。文档说它代表硬件序列号。因此,如果它存在于设备上,它应该是唯一的。

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

答案 13 :(得分:15)

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

使用:

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

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

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

我的设备没有手机广播。

答案 14 :(得分:14)

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

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

我个人认为这是可以接受但不理想的。 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();
    }
}

答案 15 :(得分:10)

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

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

答案 16 :(得分:9)

使用TelephonyManagerANDROID_ID作为字符串的Android操作系统设备的唯一设备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建议的方法,请参阅 Identifying App Installations

答案 17 :(得分:8)

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

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

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

答案 18 :(得分:7)

以下是我如何生成唯一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;
}

答案 19 :(得分:7)

我的两分钱 - 注意这是设备(错误)唯一ID - 而不是Android developers's blog中讨论的安装设备。

值得注意的是,@ emmby提供的solution会回退到每个应用程序ID,因为SharedPreferences不会跨进程同步(请参阅herehere)。所以我完全避免了这一点。

相反,我封装了在枚举中获取(设备)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);
        }
    }
}

答案 20 :(得分:7)

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

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

3个ID是:
伪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 "";
}

答案 21 :(得分:7)

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

答案 22 :(得分:6)

当设备没有电话功能时,我使用以下代码获取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);

答案 23 :(得分: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

您还可以看到我的博客文章 Leaking Android hardware serial number to unprivileged apps ,其中我讨论了哪些其他文件可供参考。

答案 24 :(得分:6)

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

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

答案 25 :(得分:6)

对于特定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的列表中获得的一堆其他接口但是这可以在不同的电话之间进行更改。

答案 26 :(得分:5)

这是一个简单的问题,没有简单的答案。

此外,这里所有现有的答案都是过时的还是不可靠的。

因此,对于那些在2019年底之前寻求真正解决方案的人。

请记住以下几点:

根据官方文档Android identifiers的最佳做法:

  

1)避免使用硬件标识符,例如SSAID(Android ID)和IMEI,MAC地址等...

这使上面的大多数答案无效。同样由于不同的android安全更新,其中一些需要更新的和更严格的运行时权限,这会带来更多问题。

例如CVE-2018-9489,它会影响上述所有基于WIFI的技术。

所以请勿使用这些技术

  

2)仅将广告ID用于用户配置文件或广告用例

不仅不可靠地识别设备,而且还必须遵守user privacy regarding ad tracking策略,该策略明确指出用户可以随时重置或阻止它。

所以也不要使用它

  

3)在可能的所有其他用例中尽可能使用FirebaseInstanceId或私有存储的GUID ,除了防止付款欺诈和电话服务。

要使用FirebaseInstanceId,请将以下依赖项添加到gradle中

implementation 'com.google.firebase:firebase-messaging:20.0.1'

并在后台线程中使用以下代码:

String reliableIdentifier = FirebaseInstanceId.getInstance().getId();

如果您需要将设备标识存储在远程服务器上,则不要按原样存储(纯文本),而是存储hash with salt

今天,这不仅是一种最佳做法,实际上您还必须根据GDPR - identifiers和类似法规依法这样做。

答案 27 :(得分:5)

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

答案 28 :(得分:5)

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

答案 29 :(得分:5)

Google实例ID

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

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

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运行时,为每个安装(details here)生成一个新的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秒可能会令人讨厌(当然,取决于您的应用)。

答案 30 :(得分:4)

借助以下功能获取D 新的UUID,带有品牌名称的型号及其版本号

在Android 10中完美运行,无需允许读取手机状态权限。

代码段:

private void fetchDeviceInfo() {
    String uniquePseudoID = "35" +
            Build.BOARD.length() % 10 +
            Build.BRAND.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 serial = Build.getRadioVersion();
    String uuid=new UUID(uniquePseudoID.hashCode(), serial.hashCode()).toString();
    String brand=Build.BRAND;
    String modelno=Build.MODEL;
    String version=Build.VERSION.RELEASE;
    Log.e(TAG, "fetchDeviceInfo: \n "+
            "\n uuid is : "+uuid+
            "\n brand is: "+brand+
            "\n model is: "+modelno+
            "\n version is: "+version);
}

调用Above函数并检查上述代码的输出。请在android studio中查看您的日志猫。如下所示:

enter image description here

答案 31 :(得分:4)

1。您可以通过gms服务访问android设备ID。参见下面的示例

private DeviceInfoProvider mDeviceInfo = new DeviceInfoProvider(Context)
String mDeviceId = DeviceInfoProvider.getDeviceId(Context);
Log.d("DEVICE_ID" , mDeviceId);

2。使用电话管理器,该管理器提供唯一的ID(即IMEI)。参见示例

import android.telephony.TelephonyManager;
import android.content.Context;
// ...
TelephonyManager telephonyManager;
telephonyManager = (TelephonyManager) getSystemService(Context.
                TELEPHONY_SERVICE);
/*
* getDeviceId() returns the unique device ID.
* For example,the IMEI for GSM and the MEID or ESN for CDMA phones.
*/
String deviceId = telephonyManager.getDeviceId();
/*
* getSubscriberId() returns the unique subscriber ID,
*/
String subscriberId = telephonyManager.getSubscriberId();

这要求您的用户使用android.permission.READ_PHONE_STATE,这可能很难根据您所创建的应用程序的类型来证明。

  1. 没有电话服务的设备(例如平板电脑)必须报告一个唯一的设备ID,该ID可从Android 2.3 Gingerbread开始通过android.os.Build.SERIAL获得。某些具有电话服务的电话也可以定义序列号。就像不是所有的Android设备都有序列号一样,此解决方案也不可靠。

  2. 在设备第一次启动时,将生成并存储一个随机值。可通过Settings.Secure.ANDROID_ID使用此值。这是一个64位数字,应在设备的整个使用寿命内保持不变。对于唯一的设备标识符,ANDROID_ID似乎是一个不错的选择,因为它可用于智能手机和平板电脑。要检索该值,可以使用以下代码,

    String androidId = Settings.Secure.getString(getContentResolver(),                  Settings.Secure.ANDROID_ID);

但是,如果在设备上执行了出厂重置,则该值可能会更改。制造商流行的手机也存在一个已知错误,其中每个实例都具有相同的ANDROID_ID。显然,该解决方案并非100%可靠。

  1. 使用UUID。由于大多数应用程序的要求是识别特定的安装而不是物理设备,因此,如果使用UUID类,则为用户获取唯一ID的一种很好的解决方案。 Google的Reto Meier在Google I / O演示中提出了以下解决方案,

SharedPreferences sharedPrefs = context.getSharedPreferences( PREF_UNIQUE_ID, Context.MODE_PRIVATE); uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);

答案 32 :(得分:4)

Google现在有一个Advertising ID 这也可以使用,但请注意:

  

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

  

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

因此,虽然此ID可能会发生变化,但似乎很快we may not have a choice,取决于此ID的目的。

More info @ develper.android

Copy-paste code here

HTH

答案 33 :(得分:3)

不推荐使用,因为deviceId可用作第三方手中的跟踪,但这是另一种方式。

@SuppressLint("HardwareIds")
private String getDeviceID() {
    deviceId = Settings.Secure.getString(getApplicationContext().getContentResolver(),
                    Settings.Secure.ANDROID_ID);
    return deviceId;
}

答案 34 :(得分:2)

几年前我遇到过这个问题,并且已经学会根据各种答案实施一个通用的解决方案。

我在现实世界的产品中使用了通用解决方案已有好几年了。到目前为止,它对我很有帮助。这是代码片段,基于各种提供的答案。

注意,getEmail大部分时间都会返回null,因为我们没有明确要求许可。

private static UniqueId getUniqueId() {
    MyApplication app = MyApplication.instance();

    // Our prefered method of obtaining unique id in the following order.
    // (1) Advertising id
    // (2) Email
    // (2) ANDROID_ID
    // (3) Instance ID - new id value, when reinstall the app.

    ////////////////////////////////////////////////////////////////////////////////////////////
    // ADVERTISING ID
    ////////////////////////////////////////////////////////////////////////////////////////////
    AdvertisingIdClient.Info adInfo = null;
    try {
        adInfo = AdvertisingIdClient.getAdvertisingIdInfo(app);
    } catch (IOException e) {
        Log.e(TAG, "", e);
    } catch (GooglePlayServicesNotAvailableException e) {
        Log.e(TAG, "", e);
    } catch (GooglePlayServicesRepairableException e) {
        Log.e(TAG, "", e);
    }

    if (adInfo != null) {
        String aid = adInfo.getId();
        if (!Utils.isNullOrEmpty(aid)) {
            return UniqueId.newInstance(aid, UniqueId.Type.aid);
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    // EMAIL
    ////////////////////////////////////////////////////////////////////////////////////////////
    final String email = Utils.getEmail();
    if (!Utils.isNullOrEmpty(email)) {
        return UniqueId.newInstance(email, UniqueId.Type.eid);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    // ANDROID ID
    ////////////////////////////////////////////////////////////////////////////////////////////
    final String sid = Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID);
    if (!Utils.isNullOrEmpty(sid)) {
        return UniqueId.newInstance(sid, UniqueId.Type.sid);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    // INSTANCE ID
    ////////////////////////////////////////////////////////////////////////////////////////////
    final String iid = com.google.android.gms.iid.InstanceID.getInstance(MyApplication.instance()).getId();
    if (!Utils.isNullOrEmpty(iid)) {
        return UniqueId.newInstance(iid, UniqueId.Type.iid);
    }

    return null;
}

public final class UniqueId implements Parcelable {
    public enum Type implements Parcelable {
        aid,
        sid,
        iid,
        eid;

        ////////////////////////////////////////////////////////////////////////////
        // Handling Parcelable nicely.

        public static final Parcelable.Creator<Type> CREATOR = new Parcelable.Creator<Type>() {
            public Type createFromParcel(Parcel in) {
                return Type.valueOf(in.readString());
            }

            public Type[] newArray(int size) {
                return new Type[size];
            }
        };

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel parcel, int flags) {
            parcel.writeString(this.name());
        }

        // Handling Parcelable nicely.
        ////////////////////////////////////////////////////////////////////////////
    }

    public static boolean isValid(UniqueId uniqueId) {
        if (uniqueId == null) {
            return false;
        }
        return uniqueId.isValid();
    }

    private boolean isValid() {
        return !org.yccheok.jstock.gui.Utils.isNullOrEmpty(id) && type != null;
    }

    private UniqueId(String id, Type type) {
        if (org.yccheok.jstock.gui.Utils.isNullOrEmpty(id) || type == null) {
            throw new java.lang.IllegalArgumentException();
        }
        this.id = id;
        this.type = type;
    }

    public static UniqueId newInstance(String id, Type type) {
        return new UniqueId(id, type);
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + id.hashCode();
        result = 31 * result + type.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof UniqueId)) {
            return false;
        }

        UniqueId uniqueId = (UniqueId)o;
        return this.id.equals(uniqueId.id) && this.type == uniqueId.type;
    }

    @Override
    public String toString() {
        return type + ":" + id;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Handling Parcelable nicely.

    public static final Parcelable.Creator<UniqueId> CREATOR = new Parcelable.Creator<UniqueId>() {
        public UniqueId createFromParcel(Parcel in) {
            return new UniqueId(in);
        }

        public UniqueId[] newArray(int size) {
            return new UniqueId[size];
        }
    };

    private UniqueId(Parcel in) {
        this.id = in.readString();
        this.type = in.readParcelable(Type.class.getClassLoader());
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(this.id);
        parcel.writeParcelable(this.type, 0);
    }

    // Handling Parcelable nicely.
    ////////////////////////////////////////////////////////////////////////////

    public final String id;
    public final Type type;
}

public static String getEmail() {
    Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
    AccountManager accountManager = AccountManager.get(MyApplication.instance());
    Account[] accounts = accountManager.getAccountsByType("com.google");
    for (Account account : accounts) {
        if (emailPattern.matcher(account.name).matches()) {
            String possibleEmail = account.name;
            return possibleEmail;
        }
    }

    accounts = accountManager.getAccounts();
    for (Account account : accounts) {
        if (emailPattern.matcher(account.name).matches()) {
            String possibleEmail = account.name;
            return possibleEmail;
        }
    }

    return null;
} 

答案 35 :(得分:2)

这是获得AAID的简单答案,经过测试,可以在2019年6月正常运行

 AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
        @Override
        protected String doInBackground(Void... params) {
            String token = null;
            Info adInfo = null;
            try {
                adInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext());
            } catch (IOException e) {
                // ...
            } catch ( GooglePlayServicesRepairableException e) {
                // ...
            } catch (GooglePlayServicesNotAvailableException e) {
                // ...
            }
            String android_id = adInfo.getId();
            Log.d("DEVICE_ID",android_id);

            return android_id;
        }

        @Override
        protected void onPostExecute(String token) {
            Log.i(TAG, "DEVICE_ID Access token retrieved:" + token);
        }

    };
    task.execute();

详细阅读完整的答案here

答案 36 :(得分:2)

了解Android设备中可用的唯一ID。使用此官方指南。

唯一标识符的最佳做法:

IMEI,Mac地址,实例ID,GUID,SSAID,广告ID,用于验证设备的Safety Net API。

https://developer.android.com/training/articles/user-data-ids

答案 37 :(得分:1)

通常,我会为我的应用使用设备唯一ID。但有时我使用IMEI。两者都是唯一的数字。

获取 IMEI 国际移动设备标识

public String getDeviceUniqueID(Activity activity){
    String device_unique_id = Secure.getString(activity.getContentResolver(),
            Secure.ANDROID_ID);
    return device_unique_id;
}

获取设备唯一ID

mapView.tintColor = .red

答案 38 :(得分:1)

android.telephony.TelephonyManager.getDeviceId()

这将返回唯一标识设备的任何字符串(GSM上的IMEI,CDMA上的MEID)。

您在AndroidManifest.xml中需要以下权限:

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

答案 39 :(得分:1)

是,每个Android设备都有一个唯一的序列号,您可以从下面的代码中获取它。 构建串行  但是请注意,它仅在API级别9中添加,并且可能并非在所有设备上都存在。要在早期平台上获得唯一ID,您需要读取诸如MAC地址或IMEI之类的内容。

答案 40 :(得分:1)

为了包含 Android 9 ,我只有一个想法仍然可行,那就是(可能)不违反任何条款,需要权限并且可以在安装和应用程序中使用。

涉及服务器的

指纹应该能够唯一地标识设备。 硬件信息+已安装的应用程序和安装时间的组合应该可以解决问题。 除非卸载并重新安装应用程序,否则第一次安装时间不会改变。但这必须对设备上的所有应用都必须执行,以便无法识别设备(即恢复出厂设置后)。

这就是我要做的事情:

  1. 提取硬件信息,应用程序包名称和首次安装时间。

这是您从Android中提取所有应用程序的方式(无需权限):

final PackageManager pm = application.getPackageManager();
List<ApplicationInfo> packages = 
pm.getInstalledApplications(PackageManager.GET_META_DATA);

for (ApplicationInfo packageInfo : packages) {
    try {
        Log.d(TAG, "Installed package :" + packageInfo.packageName);
        Log.d(TAG, "Installed :" + pm.getPackageInfo(packageInfo.packageName, 0).firstInstallTime);
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
}
  1. 您可能希望在将每个软件包名称和安装时间戳组合发送给服务器之前对其进行哈希处理,因为它可能与用户在设备上安装的内容无关,与您无关。
  2. 某些应用程序(实际上很多)是系统应用程序。它们可能具有相同的安装时间戳,与出厂重置后的最新系统更新匹配。由于它们具有相同的安装时间戳,因此用户无法安装它们,因此可以将其过滤掉。
  3. 将信息发送到服务器,并使其在先前存储的信息中查找最匹配的信息。在安装和卸载应用程序时,与以前存储的设备信息进行比较时,需要设定一个阈值。但我的猜测是,此阈值可能非常低,因为任何软件包名称和首次安装时间戳组合对于设备而言都是非常独特的,并且应用程序安装和卸载的频率并不高。拥有多个应用只会增加独特性的可能性。
  4. 返回为比赛生成的唯一ID,或生成唯一ID,将其与设备信息一起存储并返回此新ID。

注意::这是未经测试且未经验证的方法!我相信它会奏效,但我也很确定如果成功了,他们会以某种方式将其关闭。

答案 41 :(得分:1)

序列号是通过android.os.Build.SERIAL提供的唯一设备ID。

public static String getSerial() {
    String serial = "";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
        serial = Build.getSerial();
    }else{ 
        serial = Build.SERIAL;    
    }
    return serial;
}

在调用getSerial()之前,请确保您具有 READ_PHONE_STATE 权限。

注意: - 不适用于没有电话的设备(如仅限wifi平板电脑)。

答案 42 :(得分:1)

String SERIAL_NUMER = Build.SERIAL;

将SERIAL NUMBER作为字符串返回,该字符串在每个设备中都是唯一的。

答案 43 :(得分:1)

为所有阅读的人寻找更多最新信息。使用Android O,系统管理这些ID的方式会有一些变化。

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

tl; dr Serial将需要PHONE权限,Android ID将根据其包名和签名针对不同的应用进行更改。

Google还整理了一份很好的文档,提供了有关何时使用硬件和软件ID的建议。

https://developer.android.com/training/articles/user-data-ids.html

答案 44 :(得分:0)

为完整起见,这是在Id和C#中获取Xamarin.Android的方法:

var id = Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId);

或者如果您不在Activity内:

var id = Settings.Secure.GetString(context.ContentResolver, Settings.Secure.AndroidId);

context是在上下文中传递的位置。

答案 45 :(得分:0)

如果要添加:

Settings.Secure.getString(context.contentResolver,
    Settings.Secure.ANDROID_ID)

Android Lint必须给您以下警告:

  

不建议使用getString获取设备标识符。   检查信息:不建议使用这些设备标识符   除了用于高价值欺诈预防和高级电话之外   用例。对于广告用例,请使用   AdvertisingIdClient $ Info#getId并用于分析,请使用   InstanceId#getId。

因此,您应该避免使用它。

Android Developer documentation中所述:

  

1:避免使用硬件标识符。

     

在大多数情况下,您可以避免使用硬件标识符,例如SSAID(Android ID)和IMEI,   而不限制所需的功能。

     

2:仅将广告ID用于用户配置文件或广告用例。

     

使用广告ID时,请始终尊重用户的选择   关于广告跟踪。另外,请确保标识符不能为   连接到个人身份信息(PII),并避免   桥接广告ID重置。

     

3:对于所有其他用例,请尽可能使用实例ID或私有存储的GUID(防止付款欺诈和

除外)      

电话。   对于绝大多数非广告用例,实例ID   或GUID就足够了。

     

4:使用适合您的用例的API以最大程度地降低隐私风险。

     

使用DRM API进行高价值的内容保护和   用于滥用保护的SafetyNet API。 SafetyNet API是   确定设备是否为真品的最简单方法   隐私风险。

我希望这会有所帮助。

答案 46 :(得分:0)

要获取用户ID,您可以使用Google Play许可库。

要下载此库,请打开SDK Manager => SDK Tools。 下载的库文件的路径是:

  

path_to_android_sdk_on_your_pc / extras / google / market_licensing / library

将库包含在您的项目中(您可以简单地复制其文件)。

接下来,您需要实现Policy接口的某些实现(您可以简单地使用库中的两个文件之一:ServerManagedPolicyStrictPolicy)。

将在processServerResponse()函数中为您提供用户ID:

public void processServerResponse(int response, ResponseData rawData) {
    if(rawData != null) {
        String userId = rawData.userId
        // use/save the value
    }
    // ...
}

接下来,您需要使用策略构造LicenseChecker并调用checkAccess()函数。以MainActivity.java为例。 MainActivity.java位于此文件夹内:

  

path_to_android_sdk_on_your_pc / extras / google / market_licensing / sample / src / com / example / android / market / licensing

别忘了向您的AndroidManifest.xml添加CHECK_LICENSE权限。

有关许可库的更多信息:https://developer.android.com/google/play/licensing

答案 47 :(得分:0)

仅获取设备ID一次,然后将其存储在数据库或文件中。在这种情况下,如果它是应用程序的第一次启动,它会生成一个ID并存储它。下次,它只会获取存储在文件中的ID。

答案 48 :(得分:0)

import android.provider.Settings.Secure;

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

答案 49 :(得分:0)

Android在Android O之后限制与硬件相关的ID,因此, Android_Id是唯一ID的解决方案,但在以下情况下存在问题 反射器设备将生成新的android_id以克服这一问题 问题,我们可以使用DRUMID。

val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
val drumIDByteArray = MediaDrm(WIDEVINE_UUID).getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID)

val drumID = android.util.Base64.encodeToString(drumIDByteArray,android.util.Base64.DEFAULT)

答案 50 :(得分:-1)

检查 SystemInfo.deviceUniqueIdentifier

文档: http://docs.unity3d.com/Documentation/ScriptReference/SystemInfo-deviceUniqueIdentifier.html

唯一的设备标识符。保证每个设备都是唯一的(只读)。

iOS:在iOS7之前的设备上,它将返回MAC地址的哈希值。在iOS7设备上,它将是UIDevice identifierForVendor,或者,如果因任何原因失败,则为ASIdentifierManager advertisingIdentifier。

答案 51 :(得分:-2)

您将使用以下代码获得wifi mac地址,无论您尝试连接到wifi时是否使用随机地址,以及是否打开或关闭wifi。

我从下面的链接中使用了一种方法,并做了一些小的修改以获得确切的地址,而不是随机的地址:

Getting MAC address in Android 6.0

public static String getMacAddr() {
StringBuilder res1 = new StringBuilder();
try {
List<NetworkInterface> all =     
Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {    
if (!nif.getName().equalsIgnoreCase("p2p0")) continue;

        byte[] macBytes = nif.getHardwareAddress();
        if (macBytes == null) {
            continue;
        }

        res1 = new StringBuilder();
        for (byte b : macBytes) {
            res1.append(String.format("%02X:",b));
        }

        if (res1.length() > 0) {
            res1.deleteCharAt(res1.length() - 1);
        }
    }
} catch (Exception ex) {
}
return res1.toString();

}