android - id长度 - imei设备id
是否有唯一的Android设备ID? (20)
最后更新时间:2015年6月2日
在阅读关于创建唯一ID,Google开发人员博客和Android文档的每篇Stack Overflow帖子后,我觉得好像'Pseudo ID'是最好的选择。
主要问题:硬件与软件
硬件
- 用户可以更改他们的硬件,Android平板电脑或手机,因此基于硬件的唯一ID不是跟踪用户的好主意
- 对于TRACKING HARDWARE ,这是一个好主意
软件
- 如果root用户可以擦除/更改他们的ROM
- 您可以跨平台(iOS,Android,Windows和Web)跟踪用户
- 最好的想要在他们同意的情况下 追踪个人用户只需让他们登录(使用OAuth使其无缝)
Android的总体细分
- 保证API> = 9/10的唯一性(包括root设备)(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 unique ID of build information (may overlap data - API < 9)
感谢@stansult发布我们的所有选项 (在此Stack Overflow问题中)。
选项列表 - 原因/为何不使用它们:
用户电子邮件 - 软件
- 用户可以更改电子邮件 - 极不可能
- API 5+
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
或 - API 14+
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
( 如何获取Android设备的主要电子邮件地址 )
用户电话号码 - 软件
- 用户可以更改电话号码 - 极不可能
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - 硬件 (只有手机,需要
android.permission.READ_PHONE_STATE
)- 大多数用户讨厌在许可中说“电话”的事实。 一些用户给出了不好的评级,因为他们认为你只是窃取他们的个人信息,当你真正想做的就是跟踪设备安装。 很明显,您正在收集数据。
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android ID - 硬件 (可以为null,可以在出厂重置时更改,可以在有根设备上更改)
- 由于它可以为'null',我们可以检查'null'并更改其值,但这意味着它将不再是唯一的。
- 如果您的用户具有出厂重置设备,则该设备上的值可能已更改或更改,因此如果您正在跟踪用户安装,则可能存在重复条目。
WLAN MAC地址 - 硬件 (需要
android.permission.ACCESS_WIFI_STATE
)- 这可能是第二个最佳选择,但您仍在收集和存储直接来自用户的唯一标识符。 很明显,您正在收集数据。
-
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
蓝牙MAC地址 - 硬件 (带蓝牙的设备,需要
android.permission.BLUETOOTH
)- 市场上的大多数应用程序都不使用蓝牙,因此如果您的应用程序不使用蓝牙而您包含此功能,则用户可能会产生怀疑。
-
<uses-permission android:name="android.permission.BLUETOOTH "/>
伪唯一ID - 软件 (适用于所有Android设备)
- 很可能,可能包含碰撞 - 请参阅下面发布的方法!
- 这使您可以从用户那里获得“几乎唯一”的ID,而无需使用任何私有ID。 您可以从设备信息创建自己的匿名ID。
我知道没有任何“完美”的方法可以在不使用权限的情况下获取唯一ID; 但是,有时我们只需要跟踪设备安装。 在创建唯一ID时,我们可以根据Android API提供的信息创建“伪唯一ID”,而无需使用额外的权限。 通过这种方式,我们可以向用户展示尊重并尝试提供良好的用户体验。
使用伪唯一ID,您实际上只会遇到基于类似设备这一事实可能存在重复的事实。 您可以调整组合方法,使其更加独特; 但是,一些开发人员需要跟踪设备安装,这将基于类似设备执行技巧或性能。
API> = 9:
如果他们的Android设备是API 9或更高版本,由于“Build.SERIAL”字段,这保证是唯一的。
请记住 ,从技术上讲,只有0.5%的API <9的用户错过了。 所以你可以专注于其余的:这是99.5%的用户!
API <9:
如果用户的Android设备低于API 9; 希望他们没有完成工厂重置,他们的'Secure.ANDROID_ID'将被保留或不是'null'。 (见http://developer.android.com/about/dashboards/index.html )
如果一切都失败了:
如果所有其他方法都失败了,如果用户确实低于API 9(低于Gingerbread),重置了他们的设备或'Secure.ANDROID_ID'返回'null',那么返回的ID将完全基于他们的Android设备信息。 这是碰撞可能发生的地方。
变化:
- 由于工厂重置而删除'Android.SECURE_ID'可能会导致值发生变化
- 编辑代码以更改API
- 改变了伪
请看下面的方法:
/**
* 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 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
重要:
当Google Play服务可用时,广告ID旨在完全取代其他标识符的现有使用,以用于广告目的(例如在Settings.Secure中使用ANDROID_ID)。 Google Play服务不可用的情况由getAdvertisingIdInfo()引发的GooglePlayServicesNotAvailableException指示。
警告,用户可以重置:
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
我试图引用我从中获取信息的每个链接。 如果您遗失并需要加入,请发表评论!
Google Player服务实例ID
Android设备是否具有唯一的ID,如果是这样,使用Java访问它的简单方法是什么?
Android操作系统设备的唯一设备ID为String,使用TelephonyManager
和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建议的方法,请参阅识别应用安装。
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运行时,将为每个安装生成一个新的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秒可能会令人讨厌(当然,取决于您的应用程序)。
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;
}
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"/>
以下是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,并在设备被擦除甚至更换后坚持使用。我计划使用这个在未来的分析中(换句话说,我还没有完成那一点:)。
使用下面的代码,您可以将Android OS设备的唯一设备ID作为字符串获取。
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
在谷歌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现在也将在安装期间保持不变。
有关此方法的更多信息,请参阅talk。
有关如何实施备份代理的完整详细信息,请参阅数据备份。我特别推荐测试底部的部分,因为备份不会立即发生,所以要测试你必须强制备份。
官方的Android开发者博客现在有一篇关于这个主题的完整文章, android-developers.blogspot.com/2011/03/… 。
对于特定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,此代码也能正常工作。
PS他们是从包含MACS的列表中获得的一堆其他接口但这可以在手机之间切换。
我要补充一点 - 我有一个独特的情况。
使用:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
事实证明,即使我的Viewsonic G Tablet报告的DeviceID不是Null,每个G Tablet也会报告相同的数字。
使用“Pocket Empires”让它变得有趣,它可让您根据“唯一”DeviceID即时访问某人的帐户。
我的设备没有手机收音机。
我认为这肯定是为一个独特的ID构建骨架的方法......检查一下。
伪唯一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
大多数构建成员都是字符串,我们在这里做的是获取它们的长度并通过模数转换它。我们有13个这样的数字,我们在前面添加两个(35)以具有与IMEI(15位数)相同的大小ID。这里有其他可能性很好,只需看看这些字符串。返回类似的东西355715565309247
。无需特殊许可,这种方法非常方便。
(额外信息:上面给出的技术是从here上的一篇文章中复制的。)
这里有相当有用的信息。
它涵盖了五种不同的ID类型:
- IMEI (仅适用于使用电话的Android设备;需要
android.permission.READ_PHONE_STATE
) - 伪唯一ID (适用于所有Android设备)
- Android ID (可以为null,可以在出厂重置时更改,可以在root电话上更改)
- WLAN MAC地址字符串(需要
android.permission.ACCESS_WIFI_STATE
) - BT MAC地址字符串(带蓝牙的设备,需要
android.permission.BLUETOOTH
)
IMEI怎么样?这对Android或其他移动设备来说是独一无二的。
以下代码使用隐藏的Android API返回设备序列号。但是,此代码不适用于Samsung 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) {
}
另一种方法是/sys/class/android_usb/android0/iSerial
在没有任何权限的应用程序中使用。
[email protected]:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root root 4096 2013-01-10 21:08 iSerial
[email protected]:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5
要在Java中执行此操作,只需使用FileInputStream打开iSerial文件并读出字符即可。请确保将其包装在异常处理程序中,因为并非所有设备都具有此文件。
至少以下设备已知此文件具有全局可读性:
- Galaxy Nexus
- Nexus S.
- 摩托罗拉Xoom 3G
- 东芝AT300
- HTC One V.
- 迷你MK802
- 三星Galaxy S II
您还可以看到我的博客发布泄漏Android硬件序列号到非特权应用程序,在那里我讨论哪些其他文件可用于获取信息。
我使用以下代码来获取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);
我的两分钱 - 注意这是设备(错误)的唯一ID - 而不是Android开发人员博客中讨论的安装ID。
值得注意的是,@ emmby提供的solution会回退到每个应用程序ID,因为SharedPreferences不会跨进程同步(请参阅here和here)。所以我完全避免了这一点。
相反,我封装了在枚举中获取(设备)ID的各种策略 - 更改枚举常量的顺序会影响获取ID的各种方式的优先级。返回第一个非null ID或抛出异常(根据不给出null含义的优秀Java实践)。所以例如我首先使用TELEPHONY - 但是一个很好的默认选择是ANDROID_ID beta:
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;
}
有关如何为安装应用程序的每个Android设备获取唯一标识符的详细说明,请参阅官方Android开发人员博客发布android-developers.blogspot.com/2011/03/…。
看来最好的方法是在安装时自己生成一个,然后在重新启动应用程序时读取它。
我个人觉得这个可以接受但不理想。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();
}
}
有很多不同的方法可以解决这些ANDROID_ID
问题(null
有时可能或特定模型的设备总是返回相同的ID),有利有弊:
- 实现自定义ID生成算法(基于应该是静态且不会更改的设备属性 - >谁知道)
- 滥用其他ID,如IMEI,序列号,Wi-Fi /蓝牙MAC地址(它们不会存在于所有设备上或需要其他权限)
我自己更喜欢使用Android 的现有OpenUDID实现(请参阅https://github.com/ylechelle/OpenUDID)(请参阅https://github.com/vieux/OpenUDID)。它易于集成,并利用ANDROID_ID
上述问题的后备。