Android设备是否具有唯一的ID,如果是这样,使用Java访问它的简单方法是什么?
Android设备是否具有唯一的ID,如果是这样,使用Java访问它的简单方法是什么?
Settings.Secure#ANDROID_ID
返回Android ID作为 每个用户都是唯一的 64位十六进制字符串。
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
UPDATE:截至最近的Android版本,很多问题都有 ANDROID_ID
已经解决了,我相信这种方法已经不再需要了。请看一下 安东尼的回答。
完全披露:我的应用程序最初使用了以下方法,但不再使用此方法,我们现在使用的方法 Android开发者博客 那个 艾姆比的回答 链接(即生成和保存) UUID#randomUUID()
)。
这个问题有很多答案,其中大部分只会在某些时候起作用,不幸的是,这还不够好。
基于我对设备的测试(所有电话,其中至少有一部分未激活):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(如预期的那样)ANDROID_ID
ANDROID_ID
和 TelephonyManager.getDeviceId()
- 只要 设置过程中添加了Google帐户。因此,如果您想要设备本身的独特之处, 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" />
导入库
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
在阅读关于创建唯一ID,Google开发人员博客和Android文档的每篇Stack Overflow帖子后,我觉得好像'Pseudo ID'是最好的选择。
Psuedo代码:
if API >= 9/10: (99.5% of devices)
return unique ID containing serial id (rooted devices may be different)
else
return unique ID of build information (may overlap data - API < 9)
感谢@stansult的发帖 我们所有的选择 (在此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 - 硬件 (可以为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时,我们可以根据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 Developer的控制台:
从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旨在完全取代现有的广告ID 出于广告目的使用其他标识符(例如使用ANDROID_ID 在“设置。安全”中,当Google Play服务可用时。案例 Google Play服务不可用的地方由a表示 引发的GooglePlayServicesNotAvailableException getAdvertisingIdInfo()。
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,并在擦除或甚至更换设备后留下来。我打算在未来的分析中使用它(换句话说,我还没有完成那一点:)。
您也可以考虑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
)