是否有唯一的 Android 设备 ID?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2785485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a unique Android device ID?
提问by Tyler
Do Android devices have a unique ID, and if so, what is a simple way to access it using Java?
Android 设备是否有唯一 ID,如果有,使用 Java 访问它的简单方法是什么?
采纳答案by Anthony Forloney
Settings.Secure#ANDROID_ID
returns the Android ID as an unique for each user64-bit hex string.
Settings.Secure#ANDROID_ID
返回 Android ID 作为每个用户的唯一64 位十六进制字符串。
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
回答by Joe
UPDATE: As of recent versions of Android, many of the issues with ANDROID_ID
have been resolved, and I believe this approach is no longer necessary. Please take a look at Anthony's answer.
更新:在最近的 Android 版本中,许多问题ANDROID_ID
已经得到解决,我相信这种方法不再需要了。请看看安东尼的回答。
Full disclosure: my app used the below approach originally but no longer uses this approach, and we now use the approach outlined in the Android Developer Blogentry that emmby's answerlinks to (namely, generating and saving a UUID#randomUUID()
).
完全披露:我的应用程序最初使用了以下方法,但不再使用这种方法,我们现在使用emmby 的答案链接到的Android 开发者博客条目中概述的方法(即,生成并保存一个)。UUID#randomUUID()
There are many answers to this question, most of which will only work "some" of the time, and unfortunately that's not good enough.
这个问题有很多答案,其中大部分只在“某些”时间有效,不幸的是这还不够好。
Based on my tests of devices (all phones, at least one of which is not activated):
根据我对设备的测试(所有手机,至少其中一部未激活):
- All devices tested returned a value for
TelephonyManager.getDeviceId()
- All GSM devices (all tested with a SIM) returned a value for
TelephonyManager.getSimSerialNumber()
- All CDMA devices returned null for
getSimSerialNumber()
(as expected) - All devices with a Google account added returned a value for
ANDROID_ID
- All CDMA devices returned the same value (or derivation of the same value) for both
ANDROID_ID
andTelephonyManager.getDeviceId()
-- as long asa Google account has been added during setup. - I did not yet have a chance to test GSM devices with no SIM, a GSM device with no Google account added, or any of the devices in airplane mode.
- 所有测试的设备都返回了一个值
TelephonyManager.getDeviceId()
- 所有 GSM 设备(均使用 SIM 卡进行测试)返回一个值
TelephonyManager.getSimSerialNumber()
- 所有 CDMA 设备都为
getSimSerialNumber()
(如预期的那样)返回 null - 添加了 Google 帐户的所有设备都返回了一个值
ANDROID_ID
- 所有CDMA设备同时用于返回的值相同(或同等价值的推导)
ANDROID_ID
和TelephonyManager.getDeviceId()
-只要一个谷歌帐户设置过程中被添加。 - 我还没有机会测试没有 SIM 卡的 GSM 设备、没有添加 Google 帐户的 GSM 设备或任何处于飞行模式的设备。
So if you want something unique to the device itself, TM.getDeviceId()
shouldbe sufficient. Obviously some users are more paranoid than others, so it might be useful to hash 1 or more of these identifiers, so that the string is still virtually unique to the device, but does not explicitly identify the user's actual device. For example, using String.hashCode()
, combined with a UUID:
所以如果你想要一些独特的设备本身,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();
might result in something like: 00000000-54b3-e7c7-0000-000046bffd97
可能会导致类似: 00000000-54b3-e7c7-0000-000046bffd97
It works well enough for me.
它对我来说效果很好。
As Richard mentions below, don't forget that you need permission to read the TelephonyManager
properties, so add this to your manifest:
正如 Richard 在下面提到的,不要忘记您需要获得读取TelephonyManager
属性的权限,因此将其添加到您的清单中:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
import libs
导入库
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
回答by Jared Burrows
Last Updated: 6/2/15
最后更新:6/2/15
After reading every Stack Overflow post about creating a unique ID, the Google developer blog and Android documentation, I feel as if the 'Pseudo ID' is the best possible option.
在阅读了每一篇关于创建唯一 ID 的 Stack Overflow 帖子、Google 开发者博客和 Android 文档后,我觉得“伪 ID”似乎是最好的选择。
Main Issue: Hardware vs Software
主要问题:硬件与软件
Hardware
硬件
- Users can change their hardware, Android tablet or phone, so unique IDs based on hardware are not good ideas for TRACKING USERS
- For TRACKING HARDWARE, this is a great idea
- 用户可以更改他们的硬件、Android 平板电脑或手机,因此基于硬件的唯一 ID 不是跟踪用户的好主意
- 对于跟踪硬件,这是一个好主意
Software
软件
- Users can wipe/change their ROM if they are rooted
- You can track users across platforms (iOS, Android, Windows, and Web)
- The best want to TRACK AN INDIVIDUAL USERwith their consentis to simply have them login (make this seamless using OAuth)
- 用户可以擦除/更改他们的 ROM 如果他们已经植根
- 您可以跨平台(iOS、Android、Windows 和 Web)跟踪用户
- 最好在他们同意的情况下跟踪个人用户,就是让他们登录(使用 OAuth 实现无缝连接)
Overall breakdown with Android
Android 整体细分
- Guarantee uniqueness (include rooted devices) for API >= 9/10 (99.5% of Android devices)
- 保证 API >= 9/10(99.5% 的 Android 设备)的唯一性(包括 root 设备)
- No extra permissions
- 没有额外的权限
Psuedo code:
伪代码:
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)
Thanks to @stansult for posting all of our options(in this Stack Overflow question).
感谢@stansult 发布我们所有的选项(在这个 Stack Overflow 问题中)。
List of options - reasons why/ why not to use them:
选项列表 - 为什么/为什么不使用它们的原因:
User Email - Software
- User could change email - HIGHLY unlikely
- API 5+
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
or - 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)
User Phone Number - Software
- Users could change phone numbers - HIGHLY unlikely
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - Hardware(only phones, needs
android.permission.READ_PHONE_STATE
)- Most users hate the fact that it says "Phone Calls" in the permission. Some users give bad ratings, because they believe you are simply stealing their personal information when all you really want to do is track device installs. It is obvious that you are collecting data.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android ID - Hardware(can be null, can change upon factory reset, can be altered on a rooted device)
- Since it can be 'null', we can check for 'null' and change its value, but this means it will no longer be unique.
- If you have a user with a factory reset device, the value may have changed or altered on the rooted device so there may be duplicates entries if you are tracking user installs.
WLAN MAC Address - Hardware(needs
android.permission.ACCESS_WIFI_STATE
)- This could be the second best option, but you are still collecting and storing a unique identifier that comes directly from a user. This is obvious that you are collecting data.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Bluetooth MAC Address - Hardware(devices with Bluetooth, needs
android.permission.BLUETOOTH
)- Most applications on the market do not use Bluetooth, and so if your application doesn't use Bluetooth and you are including this, the user could become suspicious.
<uses-permission android:name="android.permission.BLUETOOTH "/>
Pseudo-Unique ID - Software(for all Android devices)
- Very possible, may contain collisions - See my method posted below!
- This allows you to have an 'almost unique' ID from the user without taking anything that is private. You can create your own anonymous ID from device information.
用户电子邮件 - 软件
- 用户可以更改电子邮件 - 极不可能
- 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 - 硬件(可以为空,可以在恢复出厂设置时更改,可以在有 root 权限的设备上更改)
- 由于它可以是 '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。
I know there isn't any 'perfect' way of getting a unique ID without using permissions; however, sometimes we only really need to track the device installation. When it comes to creating a unique ID, we can create a 'pseudo unique id' based solely off of information that the Android API gives us without using extra permissions. This way, we can show the user respect and try to offer a good user experience as well.
我知道没有任何“完美”的方法可以在不使用权限的情况下获得唯一 ID;然而,有时我们只需要跟踪设备安装。在创建唯一 ID 时,我们可以仅根据 Android API 提供给我们的信息创建“伪唯一 ID”,而无需使用额外权限。通过这种方式,我们可以表达对用户的尊重,并尝试提供良好的用户体验。
With a pseudo-unique id, you really only run into the fact that there may be duplicates based on the fact that there are similar devices. You can tweak the combined method to make it more unique; however, some developers need to track device installs and this will do the trick or performance based on similar devices.
使用伪唯一 id,您实际上只会遇到基于存在类似设备的事实可能存在重复项的事实。您可以调整组合方法以使其更加独特;然而,一些开发人员需要跟踪设备安装,这将根据类似设备实现技巧或性能。
API >= 9:
API >= 9:
If their Android device is API 9 or over, this is guaranteed to be unique because of the 'Build.SERIAL' field.
如果他们的 Android 设备是 API 9 或更高版本,则由于“Build.SERIAL”字段,这保证是唯一的。
REMEMBER, you are technically only missing out on around 0.5% of users who have API < 9. So you can focus on the rest: This is 99.5% of the users!
请记住,从技术上讲,您只错过了大约 0.5% 的API < 9用户。所以你可以专注于其余的:这是 99.5% 的用户!
API < 9:
API < 9:
If the user's Android device is lower than API 9; hopefully, they have not done a factory reset and their 'Secure.ANDROID_ID' will be preserved or not 'null'. (see http://developer.android.com/about/dashboards/index.html)
如果用户的Android设备低于API 9;希望他们没有进行出厂重置,并且他们的“Secure.ANDROID_ID”将被保留或不为“null”。(见http://developer.android.com/about/dashboards/index.html)
If all else fails:
如果一切都失败了:
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.
如果所有其他方法都失败了,如果用户确实低于 API 9(低于 Gingerbread),重置了他们的设备或“Secure.ANDROID_ID”返回“null”,那么返回的 ID 将完全基于他们的 Android 设备信息。这是可能发生碰撞的地方。
Changes:
变化:
- Removed 'Android.SECURE_ID' because of factory resets could cause the value to change
- Edited the code to change on API
- Changed the Pseudo
- 删除了“Android.SECURE_ID”,因为恢复出厂设置可能会导致值发生变化
- 编辑代码以更改 API
- 更改了伪
Please take a look at the method below:
请看下面的方法:
/**
* 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();
}
New (for apps with ads AND Google Play Services):
新功能(适用于带有广告和 Google Play 服务的应用):
From the Google Play Developer's console:
从 Google Play 开发者控制台:
Beginning August 1st, 2014, the Google Play Developer Program Policy requires all new app uploads and updates to use the advertising ID in lieu of any other persistent identifiers for any advertising purposes. Learn more
从 2014 年 8 月 1 日开始,Google Play 开发者计划政策要求所有新应用上传和更新使用广告 ID 代替任何其他持久标识符用于任何广告目的。了解更多
Implementation:
实施:
Permission:
允许:
<uses-permission android:name="android.permission.INTERNET" />
Code:
代码:
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();
}
Source/Docs:
来源/文档:
http://developer.android.com/google/play-services/id.htmlhttp://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
Important:
重要的:
It is intended that the advertising ID completely replace existing usage of other identifiers for ads purposes (such as the use of ANDROID_ID in Settings.Secure) when Google Play Services is available. Cases where Google Play Services is unavailable are indicated by a GooglePlayServicesNotAvailableException being thrown by getAdvertisingIdInfo().
当 Google Play 服务可用时,广告 ID 旨在完全取代其他标识符的现有广告用途(例如在 Settings.Secure 中使用 ANDROID_ID)。Google Play 服务不可用的情况由 getAdvertisingIdInfo() 抛出的 GooglePlayServicesNotAvailableException 指示。
Warning, users can reset:
警告,用户可以重置:
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
I have tried to reference every link that I took information from. If you are missing and need to be included, please comment!
我试图引用我从中获取信息的每个链接。如有遗漏需要补充,请留言!
Google Player Services InstanceID
Google 播放器服务实例 ID
回答by emmby
As Dave Webb mentions, the Android Developer Blog has an articlethat covers this. Their preferred solution is to track app installs rather than devices, and that will work well for most use cases. The blog post will show you the necessary code to make that work, and I recommend you check it out.
正如 Dave Webb 所提到的,Android Developer Blog 有一篇文章介绍了这一点。他们首选的解决方案是跟踪应用安装而不是设备,这对大多数用例都适用。这篇博文将向您展示实现该功能所需的代码,我建议您查看一下。
However, the blog post goes on to discuss solutions if you need a device identifier rather than an app installation identifier. I spoke with someone at Google to get some additional clarification on a few items in the event that you need to do so. Here's what I discovered about device identifiers that's NOT mentioned in the aforementioned blog post:
但是,如果您需要设备标识符而不是应用程序安装标识符,博客文章继续讨论解决方案。我与 Google 的某人进行了交谈,以便在您需要时对一些项目进行额外说明。以下是我发现的有关上述博客文章中未提及的设备标识符的信息:
- ANDROID_ID is the preferred device identifier. ANDROID_ID is perfectly reliable on versions of Android <=2.1 or >=2.3. Only 2.2 has the problems mentioned in the post.
- Several devices by several manufacturers are affected by the ANDROID_ID bug in 2.2.
- As far as I've been able to determine, all affected devices have the same ANDROID_ID, which is 9774d56d682e549c. Which is also the same device id reported by the emulator, btw.
- Google believes that OEMs have patched the issue for many or most of their devices, but I was able to verify that as of the beginning of April 2011, at least, it's still quite easy to find devices that have the broken ANDROID_ID.
- ANDROID_ID 是首选设备标识符。ANDROID_ID 在 Android <=2.1 或 >=2.3 版本上完全可靠。只有2.2有帖子中提到的问题。
- 多个制造商的多个设备受到 2.2 中 ANDROID_ID 错误的影响。
- 据我所知,所有受影响的设备都具有相同的 ANDROID_ID,即9774d56d682e549c。这也是模拟器报告的相同设备 ID,顺便说一句。
- Google 认为 OEM 已经为他们的许多或大部分设备修复了该问题,但我能够验证,至少在 2011 年 4 月初,找到具有损坏 ANDROID_ID 的设备仍然很容易。
Based on Google's recommendations, I implemented a class that will generate a unique UUID for each device, using ANDROID_ID as the seed where appropriate, falling back on TelephonyManager.getDeviceId() as necessary, and if that fails, resorting to a randomly generated unique UUID that is persisted across app restarts (but not app re-installations).
根据谷歌的建议,我实现了一个将为每个设备生成唯一 UUID 的类,在适当的情况下使用 ANDROID_ID 作为种子,必要时回退到 TelephonyManager.getDeviceId(),如果失败,则求助于随机生成的唯一 UUID这在应用程序重新启动(但不是应用程序重新安装)中持续存在。
Note that for devices that have to fallback on the device ID, the unique ID WILLpersist across factory resets. This is something to be aware of. If you need to ensure that a factory reset will reset your unique ID, you may want to consider falling back directly to the random UUID instead of the device ID.
请注意,对于必须回退到设备 ID 的设备,唯一 ID将在出厂重置后保持不变。这是需要注意的。如果您需要确保恢复出厂设置会重置您的唯一 ID,您可能需要考虑直接回退到随机 UUID 而不是设备 ID。
Again, this code is for a device ID, not an app installation ID. For most situations, an app installation ID is probably what you're looking for. But if you do need a device ID, then the following code will probably work for you.
同样,此代码用于设备 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;
}
}
回答by Anthony Nolan
Here is the code that Reto Meier used in the Google I/Opresentation this year to get a unique id for the user:
这是 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;
}
If you couple this with a backup strategy to send preferences to the cloud (also described in Reto's talk, you should have an id that ties to a user and sticks around after the device has been wiped, or even replaced. I plan to use this in analytics going forward (in other words, I have not done that bit yet :).
如果您将此与备份策略相结合以将首选项发送到云(也在 Reto 的演讲中进行了描述,您应该有一个与用户相关联并在设备被擦除甚至更换后仍然存在的 id。我计划使用这个在未来的分析中(换句话说,我还没有做过那一点:)。
回答by Seva Alekseyev
Also you might consider the Wi-Fi adapter's MAC address. Retrieved thusly:
您还可以考虑 Wi-Fi 适配器的 MAC 地址。如此检索:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
Requires permission android.permission.ACCESS_WIFI_STATE
in the manifest.
需要android.permission.ACCESS_WIFI_STATE
清单中的许可。
Reported to be available even when Wi-Fi is not connected. If Joe from the answer above gives this one a try on his many devices, that'd be nice.
据说即使未连接 Wi-Fi 也可用。如果上面答案中的乔在他的许多设备上尝试了这个,那就太好了。
On some devices, it's not available when Wi-Fi is turned off.
在某些设备上,当 Wi-Fi 关闭时它不可用。
NOTE:From Android 6.x, it returns consistent fake mac address: 02:00:00:00:00:00
注意:从 Android 6.x 开始,它返回一致的假 mac 地址:02:00:00:00:00:00
回答by stansult
There's rather useful info here.
有相当有用的信息在这里。
It covers five different ID types:
它涵盖五种不同的 ID 类型:
- IMEI(only for Android devices with Phone use; needs
android.permission.READ_PHONE_STATE
) - Pseudo-Unique ID(for all Android devices)
- Android ID(can be null, can change upon factory reset, can be altered on rooted phone)
- WLAN MAC Addressstring (needs
android.permission.ACCESS_WIFI_STATE
) - BT MAC Addressstring (devices with Bluetooth, needs
android.permission.BLUETOOTH
)
- IMEI(仅适用于使用手机的 Android 设备;需要
android.permission.READ_PHONE_STATE
) - 伪唯一 ID(适用于所有 Android 设备)
- Android ID(可以为空,可以在恢复出厂设置时更改,可以在root手机上更改)
- WLAN MAC 地址字符串(需要
android.permission.ACCESS_WIFI_STATE
) - BT MAC 地址字符串(带蓝牙的设备,需要
android.permission.BLUETOOTH
)
回答by BoD
The official Android Developers Blog now has a full article just about this very subject, Identifying App Installations.
官方 Android 开发者博客现在有一篇关于这个主题的完整文章,识别应用程序安装。
回答by TechnoTony
At Google I/OReto Meier released a robust answer to how to approach this which should meet most developers needs to track users across installations. Anthony Nolan shows the direction in his answer, but I thought I'd write out the full approach so that others can easily see how to do it (it took me a while to figure out the details).
在Google I/O大会上,Reto Meier 发布了一个关于如何解决这个问题的可靠答案,这应该可以满足大多数开发人员在跨安装跟踪用户的需求。Anthony Nolan 在他的回答中指明了方向,但我想我会写出完整的方法,这样其他人就可以很容易地看到如何去做(我花了一段时间才弄清楚细节)。
This approach will give you an anonymous, secure user ID which will be persistent for the user across different devices (based on the primary Google account) and across installs. The basic approach is to generate a random user ID and to store this in the apps' shared preferences. You then use Google's backup agent to store the shared preferences linked to the Google account in the cloud.
这种方法将为您提供一个匿名、安全的用户 ID,该 ID 将在不同设备(基于主 Google 帐户)和安装之间对用户保持不变。基本方法是生成一个随机用户 ID 并将其存储在应用程序的共享首选项中。然后,您可以使用 Google 的备份代理将链接到 Google 帐户的共享首选项存储在云中。
Let's go through the full approach. First, we need to create a backup for our SharedPreferences using the Android Backup Service. Start by registering your app via http://developer.android.com/google/backup/signup.html
.
让我们来看看完整的方法。首先,我们需要使用 Android 备份服务为我们的 SharedPreferences 创建一个备份。首先通过 注册您的应用程序http://developer.android.com/google/backup/signup.html
。
Google will give you a backup service key which you need to add to the manifest. You also need to tell the application to use the BackupAgent as follows:
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>
Then you need to create the backup agent and tell it to use the helper agent for sharedpreferences:
然后,您需要创建备份代理并告诉它使用共享首选项的帮助代理:
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);
}
}
To complete the backup you need to create an instance of BackupManager in your main Activity:
要完成备份,您需要在主活动中创建一个 BackupManager 实例:
BackupManager backupManager = new BackupManager(context);
Finally create a user ID, if it doesn't already exist, and store it in the SharedPreferences:
最后创建一个用户 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;
}
This User_ID will now be persistent across installations, even if the user moves device.
现在,即使用户移动设备,此 User_ID 也将在整个安装过程中保持不变。
For more information on this approach see Reto's talk.
有关此方法的更多信息,请参阅Reto 的演讲。
And for full details of how to implement the backup agent see Data Backup. I particularly recommend the section at the bottom on testing as the backup does not happen instantaneously and so to test you have to force the backup.
有关如何实施备份代理的完整详细信息,请参阅数据备份。我特别推荐底部的测试部分,因为备份不会立即发生,因此要进行测试,您必须强制备份。
回答by Lenn Dolling
I think this is sure fire way of building a skeleton for a unique ID... check it out.
我认为这肯定是为唯一 ID 构建骨架的方法……检查一下。
Pseudo-Unique ID, that works on all Android devices Some devices don't have a phone (eg. Tablets) or for some reason, you don't want to include the READ_PHONE_STATE permission. You can still read details like ROM Version, Manufacturer name, CPU type, and other hardware details, that will be well suited if you want to use the ID for a serial key check, or other general purposes. The ID computed in this way won't be unique: it is possible to find two devices with the same ID (based on the same hardware and ROM image) but the changes in real-world applications are negligible. For this purpose you can use the Build class:
伪唯一 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
Most of the Build members are strings, what we're doing here is to take their length and transform it via modulo in a digit. We have 13 such digits and we are adding two more in front (35) to have the same size ID as the IMEI (15 digits). There are other possibilities here are well, just have a look at these strings.
Returns something like 355715565309247
. No special permission is required, making this approach very convenient.
大多数 Build 成员都是字符串,我们在这里做的是获取它们的长度并通过数字取模进行转换。我们有 13 个这样的数字,我们在前面(35)再添加两个,以具有与 IMEI 相同的大小 ID(15 位)。这里还有其他可能性,看看这些字符串。返回类似355715565309247
. 不需要特殊许可,使这种方法非常方便。
(Extra info: The technique given above was copied from an article on Pocket Magic.)
(额外信息:上面给出的技术是从Pocket Magic上的一篇文章中复制的。)