Settings.Secure#ANDROID_ID
以每个用户 64 位十六进制字符串的唯一身份返回 Android ID。
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
更新 :从 Android 的最新版本开始, ANDROID_ID
许多问题已解决,我相信不再需要这种方法。请看一下安东尼的回答 。
全面披露:我的应用最初使用以下方法,但不再使用该方法,现在我们使用emmby 的答案链接到的Android 开发者博客条目中概述的方法(即,生成并保存UUID#randomUUID()
)。
这个问题有很多答案,其中大多数只会在某些时候起作用,但是不幸的是,这还不够好。
根据我对设备的测试(所有电话,其中至少一个未激活):
TelephonyManager.getDeviceId()
的值TelephonyManager.getSimSerialNumber()
的值getSimSerialNumber()
返回 null(按预期方式) ANDROID_ID
的值ANDROID_ID
和TelephonyManager.getDeviceId()
都返回相同的值(或得出相同的值TelephonyManager.getDeviceId()
- 只要在设置过程中添加了 Google 帐户即可。 因此,如果您想要设备本身特有的功能,则TM.getDeviceId()
应该足够了。显然,某些用户比其他用户更偏执,因此散列 1 个或多个这些标识符可能会很有用,因此该字符串实际上对于设备仍然是唯一的,但未明确标识用户的实际设备。例如,结合使用String.hashCode()
和 UUID:
final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);
final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
tmSerial = "" + tm.getSimSerialNumber();
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();
可能会导致以下内容: 00000000-54b3-e7c7-0000-000046bffd97
对我来说效果很好。
正如 Richard 在下面提到的那样,不要忘记您需要阅读TelephonyManager
属性的权限,因此请将其添加到清单中:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
导入库
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
阅读了有关创建唯一 ID 的所有 Stack Overflow 帖子,Google 开发者博客和 Android 文档之后,我觉得 “伪 ID” 似乎是最好的选择。
伪代码:
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 问题中)。
用户电子邮件 - 软件
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
或<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 - 硬件 (可以为空,可以在恢复出厂设置时更改,可以在有根设备上更改)
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 时,我们可以仅基于 Android API 提供给我们的信息来创建 “伪唯一 ID”,而无需使用额外的权限。这样,我们可以显示用户的尊敬,并尝试提供良好的用户体验。
使用伪唯一 ID,您实际上只会遇到这样的事实,即基于存在相似设备的事实,可能存在重复项。您可以调整组合方法以使其更加独特。但是,一些开发人员需要跟踪设备安装,这将根据相似的设备来解决问题或提高性能。
如果他们的 Android 设备是 API 9 或更高版本,则由于 “Build.SERIAL” 字段,因此可以保证其唯一。
请记住 ,从技术上讲,您仅会错过大约 0.5%的API <9用户。因此,您可以专注于其余部分:这是 99.5%的用户!
如果用户的 Android 设备低于 API 9;希望他们没有恢复出厂设置,并且他们的 “Secure.ANDROID_ID” 将保留或不为 “null”。 (请参阅http://developer.android.com/about/dashboards/index.html )
如果所有其他方法均失败,则如果用户确实低于 API 9(低于 Gingerbread),已重置其设备或 “Secure.ANDROID_ID” 返回 “null”,则返回的 ID 仅基于其 Android 设备信息。这是可能发生碰撞的地方。
变化:
请看下面的方法:
/**
* 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 开发者控制台中:
从 2014 年 8 月 1 日开始,Google Play 开发者计划政策要求所有新的应用上载和更新都必须使用广告 ID 代替任何其他永久性标识符用于任何广告目的。学到更多
实现方式 :
允许:
<uses-permission android:name="android.permission.INTERNET" />
码:
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...
// Do not call this function from the main thread. Otherwise,
// an IllegalStateException will be thrown.
public void getIdThread() {
Info adInfo = null;
try {
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
} catch (IOException exception) {
// Unrecoverable error connecting to Google Play services (e.g.,
// the old version of the service doesn't support getting AdvertisingId).
} catch (GooglePlayServicesAvailabilityException exception) {
// Encountered a recoverable error connecting to Google Play services.
} catch (GooglePlayServicesNotAvailableException exception) {
// Google Play services is not available entirely.
}
final String id = adInfo.getId();
final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}
来源 / 文件:
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
当 Google Play 服务可用时,广告 ID 旨在完全替代出于广告目的而使用的其他其他标识符(例如,在 Settings.Secure 中使用 ANDROID_ID)。由 getAdvertisingIdInfo()引发的 GooglePlayServicesNotAvailableException 指示了 Google Play 服务不可用的情况。
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
我试图参考我从中获取信息的每个链接。如果您不见了并且需要包括在内,请发表评论!
正如 Dave Webb 所提到的, Android 开发者博客上有一篇文章对此进行了介绍。他们的首选解决方案是跟踪应用程序的安装而不是设备,并且在大多数情况下都能很好地工作。博客文章将向您显示完成该工作所需的代码,我建议您检查一下。
但是,如果您需要设备标识符而不是应用程序安装标识符,那么该博客文章将继续讨论解决方案。如果需要,我与 Google 的某人进行了交谈,以进一步澄清一些事项。这是我发现的有关设备标识符的内容,在上述博客文章中未提及:
根据 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;
}
}
这是 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 的演讲中也有介绍),则应该有一个与用户相关联的 ID,并在擦除或更换设备后仍然存在。我计划使用此 ID。在未来的分析中(换句话说,我还没有做到这一点:)。
您也可以考虑 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
有相当有用的信息在这里 。
它涵盖了五种不同的 ID 类型:
android.permission.READ_PHONE_STATE
) android.permission.ACCESS_WIFI_STATE
) android.permission.BLUETOOTH
) 现在,Android 开发者官方博客正式版中有一篇完整的文章,即确定应用程序安装 。
Reto Meier 在Google I / O 上发布了一个强有力的答案,说明了如何解决此问题,这应满足大多数开发人员跟踪安装之间用户的需求。安东尼 · 诺兰(Anthony Nolan)在回答中指明了方向,但我想我会写出完整的方法,以便其他人可以轻松地看到如何做(我花了一段时间才弄清楚细节)。
这种方法将为您提供一个匿名的安全用户 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);
}
}
要完成备份,您需要在主活动中创建一个 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 的演讲 。
有关如何实施备份代理的完整详细信息,请参见数据备份 。我特别推荐测试的底部部分,因为备份不会立即发生,因此要测试您必须强制执行备份。
我认为这无疑是为唯一 ID 构建骨架的一种行之有效的方法。
在所有 Android 设备上均可使用的伪唯一 ID。某些设备没有电话(例如平板电脑),或者由于某些原因,您不希望包括 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 相同的大小 ID(15 个数字)。这里还有其他可能性,只看这些字符串即可。返回类似355715565309247
。不需要特殊许可,这使此方法非常方便。
(额外信息:以上给出的技术是从Pocket Magic上的文章中复制的。)