Tyl*_*ler 2645 android uniqueidentifier
Android设备是否具有唯一ID,如果是这样,使用Java访问它的简单方法是什么?
Ant*_*ney 1937
Settings.Secure#ANDROID_ID返回Android ID作为每个用户 64位十六进制字符串的唯一 ID .
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)
Joe*_*Joe 1123
更新:截至Android的最新版本,许多问题ANDROID_ID已经解决,我相信这种方法已不再需要.请看看安东尼的回答.
完全披露:我的应用程序最初使用了以下方法但不再使用此方法,我们现在使用Android开发人员博客条目中概述的方法,即emmby的答案链接(即生成和保存a UUID#randomUUID()).
这个问题有很多答案,其中大部分只会在某些时候起作用,不幸的是,这还不够好.
根据我对设备的测试(所有手机,其中至少有一部分未激活):
TelephonyManager.getDeviceId()TelephonyManager.getSimSerialNumber()getSimSerialNumber()(如预期的那样)ANDROID_IDANDROID_ID和TelephonyManager.getDeviceId()- 只要一个谷歌帐户设置过程中被添加.所以,如果你想要独一无二的设备本身的东西,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();
Run Code Online (Sandbox Code Playgroud)
可能会导致类似于: 00000000-54b3-e7c7-0000-000046bffd97
它对我来说效果很好.
正如Richard在下面提到的那样,不要忘记您需要获得读取TelephonyManager属性的权限,因此请将其添加到清单中:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Run Code Online (Sandbox Code Playgroud)
导入库
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
Run Code Online (Sandbox Code Playgroud)
Jar*_*ows 425
在阅读关于创建唯一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 the unique ID of build information (may overlap data - API < 9)
Run Code Online (Sandbox Code Playgroud)
感谢@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 "/>Pseudo-Unique 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();
}
Run Code Online (Sandbox Code Playgroud)
来自Google Play Developer的控制台:
从2014年8月1日开始,Google Play开发人员计划政策要求所有新的应用上传和更新都使用广告ID代替任何其他持久性标识符,以用于任何广告目的.学到更多
实施:
允许:
<uses-permission android:name="android.permission.INTERNET" />
Run Code Online (Sandbox Code Playgroud)
码:
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();
}
Run Code Online (Sandbox Code Playgroud)
来源/文档:
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
我试图引用我从中获取信息的每个链接.如果您遗失并需要加入,请发表评论!
https://developers.google.com/instance-id/
emm*_*mby 334
正如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;
}
}
Run Code Online (Sandbox Code Playgroud)
Ant*_*lan 176
以下是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;
}
Run Code Online (Sandbox Code Playgroud)
如果你把这个与备份策略结合起来将偏好发送到云端(在Reto的谈话中也有描述,你应该有一个与用户绑定的ID,并在设备被擦除甚至更换后坚持使用.我打算使用这个在未来的分析中(换句话说,我还没有完成那一点:).
Sev*_*yev 103
您也可以考虑Wi-Fi适配器的MAC地址.如此检索:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
Run Code Online (Sandbox Code Playgroud)
需要android.permission.ACCESS_WIFI_STATE清单中的权限.
报告即使未连接Wi-Fi也可用.如果上面的答案中的Joe在他的许多设备上尝试了这个,那就太好了.
在某些设备上,当Wi-Fi关闭时,它不可用.
注意:从Android 6.x开始,它会返回一致的虚假mac地址:02:00:00:00:00:00
sta*_*ult 84
有相当有用的信息在这里.
它涵盖五种不同的ID类型:
android.permission.READ_PHONE_STATE)android.permission.ACCESS_WIFI_STATE)android.permission.BLUETOOTH)Nik*_*tin 82
这是一个简单的问题,没有简单的答案。
更重要的是,这里所有现有的答案是否过时或不可靠。
因此,如果您正在寻找 2020 年的解决方案。
以下是一些需要注意的事项:
所有基于硬件的标识符(SSAID、IMEI、MAC 等)对于非谷歌的设备(Pixels 和 Nexuses 除外的所有设备)都不可靠,这些设备占全球活跃设备的 50% 以上。因此,官方Android 标识符最佳实践明确指出:
避免使用硬件标识符,例如 SSAID(Android ID)、IMEI、MAC 地址等...
这使得上面的大多数答案无效。同样由于不同的android安全更新,其中一些需要更新和更严格的运行时权限,用户可以简单地拒绝。
作为CVE-2018-9489影响上述所有基于 WIFI 的技术的示例。
这使得这些标识符不仅不可靠,而且在许多情况下也无法访问。
所以简单地说:不要使用这些技术。
这里的许多其他答案都建议使用AdvertisingIdClient,这也是不兼容的,因为它的设计应仅用于广告分析。官方参考资料中也有说明
仅将广告 ID 用于用户分析或广告用例
它不仅对设备识别不可靠,而且您还必须遵守有关广告跟踪政策的用户隐私,其中明确规定用户可以随时重置或阻止它。
所以也不要使用它。
由于您无法拥有所需的静态全局唯一且可靠的设备标识符。Android的官方参考建议:
对于所有其他用例,尽可能使用 FirebaseInstanceId 或私人存储的 GUID,支付欺诈预防和电话除外。
它对于设备上的应用程序安装来说是独一无二的,所以当用户卸载应用程序时 - 它会被清除,所以它不是 100% 可靠的,但它是次优的。
要使用FirebaseInstanceId将最新的 firebase -messaging 依赖项添加到您的 gradle 中
implementation 'com.google.firebase:firebase-messaging:20.2.4'
Run Code Online (Sandbox Code Playgroud)
并在后台线程中使用以下代码:
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
Run Code Online (Sandbox Code Playgroud)
如果您需要在远程服务器上存储设备标识,则不要按原样(纯文本)存储它,而是使用 salt进行哈希存储。
今天,这不仅是最佳实践,您实际上还必须根据GDPR - 标识符和类似规定依法执行。
Tec*_*ony 41
在谷歌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>
Run Code Online (Sandbox Code Playgroud)
然后,您需要创建备份代理并告诉它使用帮助代理进行共享优先:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
要完成备份,您需要在主Activity中创建一个BackupManager实例:
BackupManager backupManager = new BackupManager(context);
Run Code Online (Sandbox Code Playgroud)
最后创建一个用户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;
}
Run Code Online (Sandbox Code Playgroud)
即使用户移动设备,此User_ID现在也将在安装期间保持不变.
有关此方法的更多信息,请参阅Reto的演讲.
有关如何实施备份代理的完整详细信息,请参阅数据备份.我特别推荐测试底部的部分,因为备份不会立即发生,所以要测试你必须强制备份.
小智 37
我认为这肯定是为一个独特的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
Run Code Online (Sandbox Code Playgroud)
大多数构建成员都是字符串,我们在这里做的是获取它们的长度并通过模数转换它.我们有13个这样的数字,我们在前面添加两个(35)以具有与IMEI(15位数)相同的大小ID.这里有其他可能性很好,只需看看这些字符串.返回类似的东西355715565309247.无需特殊许可,这种方法非常方便.
(额外信息:上面给出的技术是从Pocket Magic上的一篇文章中复制而来的.)
小智 35
以下代码使用隐藏的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) {
}
Run Code Online (Sandbox Code Playgroud)
Moh*_*ada 25
使用下面的代码,您可以将Android OS设备的唯一设备ID作为字符串获取.
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)
Ton*_*aro 16
我要补充一点 - 我有一个独特的情况.
使用:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)
事实证明,即使我的Viewsonic G平板电脑报告的DeviceID不是Null,每个G平板电脑都报告相同的数字.
使用"Pocket Empires"让它变得有趣,它可以让您根据"唯一的"DeviceID即时访问某人的帐户.
我的设备没有手机收音机.
Kev*_*ker 15
有关如何为安装应用程序的每个Android设备获取唯一标识符的详细说明,请参阅官方Android开发人员博客发布标识应用程序安装.
看来最好的方法是在安装时自己生成一个,然后在重新启动应用程序时读取它.
我个人觉得这个可以接受但不理想.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();
}
}
Run Code Online (Sandbox Code Playgroud)
And*_*oid 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);
Run Code Online (Sandbox Code Playgroud)
在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
但我强烈推荐Google建议的方法,请参阅识别应用安装.
有很多不同的方法可以解决这些ANDROID_ID问题(可能null有时或特定模型的设备总是返回相同的ID),有利有弊:
我自己更喜欢使用Android 的现有OpenUDID实现(请参阅https://github.com/ylechelle/OpenUDID)(请参阅https://github.com/vieux/OpenUDID).它易于集成,并利用ANDROID_ID上述问题的后备.
这里有30多个答案,有些是相同的,有些是独一无二的.这个答案基于这些答案中的一小部分.其中一个是@Lenn Dolling的回答.
它结合了3个ID并创建一个32位十六进制字符串.它对我来说非常有效.
3个ID是:
Pseudo-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 "";
}
Run Code Online (Sandbox Code Playgroud)
1.使用电话管理器,它提供了一个唯一的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();
Run Code Online (Sandbox Code Playgroud)
这需要android.permission.READ_PHONE_STATE您的用户根据您制作的应用程序类型很难证明其合理性。
没有电话服务的设备(如平板电脑)必须报告android.os.Build.SERIAL自 Android 2.3 Gingerbread 起可用的唯一设备 ID 。某些具有电话服务的电话还可以定义序列号。与并非所有 Android 设备都有序列号一样,此解决方案并不可靠。
在设备首次启动时,会生成并存储一个随机值。该值可通过Settings.Secure.ANDROID_ID. 它是一个 64 位数字,应该在设备的生命周期内保持不变。ANDROID_ID似乎是唯一设备标识符的不错选择,因为它可用于智能手机和平板电脑。要检索该值,您可以使用以下代码,
String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
但是,如果在设备上执行恢复出厂设置,该值可能会更改。制造商生产的流行手机也存在一个已知错误,其中每个实例都具有相同的ANDROID_ID. 显然,该解决方案并非 100% 可靠。
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();
Run Code Online (Sandbox Code Playgroud)
更新:选项#1和#2在 android 10 之后不再可用,因为谷歌的隐私更新。因为选项 2 和 3 需要关键权限。
借助以下函数获取设备 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);
}
Run Code Online (Sandbox Code Playgroud)
调用上述函数并检查上述代码的输出。请在 android studio 中查看您的日志猫。它看起来像下面这样:
以下是我如何生成唯一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;
}
Run Code Online (Sandbox Code Playgroud)
我的两分钱 - 注意这是设备(错误)的唯一ID - 而不是Android开发者博客中讨论的安装ID.
值得注意的是,@ emmby提供的解决方案会回退到每个应用程序ID,因为SharedPreferences不会跨进程同步(请参阅此处和此处).所以我完全避免了这一点.
相反,我封装了在枚举中获取(设备)ID的各种策略 - 更改枚举常量的顺序会影响获取ID的各种方式的优先级.返回第一个非null ID或抛出异常(根据不给出null含义的优秀Java实践).所以例如我首先使用TELEPHONY - 但是一个很好的默认选择是ANDROID_ID beta:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是/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
Run Code Online (Sandbox Code Playgroud)
要在Java中执行此操作,只需使用FileInputStream打开iSerial文件并读出字符即可.请确保将其包装在异常处理程序中,因为并非所有设备都具有此文件.
至少以下设备已知此文件具有全局可读性:
您还可以看到我的博客发布泄漏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);
Run Code Online (Sandbox Code Playgroud)
对于特定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();
}
Run Code Online (Sandbox Code Playgroud)
在每个Android设备中,它们至少是一个"wlan0"接口巫婆是WI-FI芯片.即使未打开WI-FI,此代码也能正常工作.
PS他们是从包含MACS的列表中获得的一堆其他接口但这可以在手机之间切换.
小智 6
更具体地说,Settings.Secure.ANDROID_ID.这是在设备首次启动时生成并存储的64位数量.擦除设备时会重置.
ANDROID_ID似乎是唯一设备标识符的不错选择.还有一些缺点:首先,它在2.2之前的Android版本上并非100%可靠.(“Froyo”).此外,在一个主要制造商的流行手机中至少有一个被广泛观察到的错误,其中每个实例都具有相同的ANDROID_ID.
了解Android设备中可用的唯一ID。使用此官方指南。
唯一标识符的最佳做法:
IMEI,Mac地址,实例ID,GUID,SSAID,广告ID,用于验证设备的Safety Net API。
https://developer.android.com/training/articles/user-data-ids
TelephonyManger.getDeviceId()返回唯一的设备ID,例如,GSM的IMEI和CDMA电话的MEID或ESN.
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
Run Code Online (Sandbox Code Playgroud)
但我建议使用:
Settings.Secure.ANDROID_ID,它将Android ID作为唯一的64位十六进制字符串返回.
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)
有时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;
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
Google似乎打算将此ID用于识别Android,Chrome和iOS上的安装.
它识别安装而不是设备,但是再次,ANDROID_ID(这是接受的答案)现在不再识别设备.使用ARC运行时,为每个安装生成一个新的ANDROID_ID(详情请参见此处),就像这个新的实例ID一样.此外,我认为识别安装(而不是设备)是我们大多数人真正想要的.
实例ID的优点
在我看来,Google打算将它用于此目的(识别您的安装),它是跨平台的,并且可以用于许多其他目的(请参阅上面的链接).
如果您使用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秒可能会令人讨厌(当然,取决于您的应用程序).
小智 5
Android设备mac id也是一个唯一的id,如果我们格式化设备本身就不会改变,所以使用下面的代码来获取mac id
WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
String address = info.getMacAddress();
Run Code Online (Sandbox Code Playgroud)
另外,不要忘记在AndroidManifest.xml中添加适当的权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
Run Code Online (Sandbox Code Playgroud)
只是提醒每个阅读并寻找更多最新信息的人。在 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
| 归档时间: |
|
| 查看次数: |
943994 次 |
| 最近记录: |