Android设备有唯一的ID吗?如果有,使用Java访问它的简单方法是什么?
当前回答
这是一个简单的问题,没有简单的答案。
此外,这里所有现有的答案要么过时,要么不可靠。
因此,如果您正在寻找2020年后的解决方案。
以下是需要记住的几点:
所有基于硬件的标识符(IMEI、MAC、序列号等)对于非谷歌设备(Pixels和Nexuses除外)都是不可靠的,从统计上看,这些设备是世界上大多数android活动设备。因此,官方Android标识符最佳实践明确指出:
避免使用硬件标识符,如IMEI、MAC地址等。。。
这使得这里的大多数答案无效。同样由于不同的android安全更新,其中一些需要更新和更严格的运行时权限,用户可以简单地拒绝。
例如,CVE-2018-9489影响上述所有基于WIFI的技术。
这使得这些标识符不仅不可靠,而且在许多情况下无法访问。
所以简单地说:不要使用这些技术。
这里的许多其他答案都建议使用AdvertisingIdClient,这也是不兼容的,因为它是专为广告分析而设计的。官方参考文件中也有说明
仅将广告ID用于用户分析或广告用例
它不仅对设备识别不可靠,而且您还必须遵守有关广告跟踪的用户隐私政策,该政策明确规定用户可以随时重置或阻止它。
所以也不要使用它。
因为您无法获得所需的静态全局唯一和可靠的设备标识符。Android的官方参考建议:
在所有其他使用情况下,尽可能使用Firebase安装ID(FID)或私人存储的GUID,支付欺诈预防和电话除外。
它对于设备上的应用程序安装来说是独一无二的,因此当用户卸载应用程序时,它会被删除,因此它不是100%可靠,但它是次佳选择。
注意:截至目前,FirebaseInstanceId已弃用,您应改用FirebaseInstallations。
要使用FirebaseInstallations,请将最新的firebase消息传递依赖项添加到您的等级中
implementation 'com.google.firebase:firebase-messaging:23.0.0'
并使用以下代码获取firebase ID:
FirebaseInstallations.getInstance().getId().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
String firebaseIdentifier = task.getResult();
// Do what you need with firebaseIdentifier
}
});
如果您需要将设备标识存储在远程服务器上,那么不要按原样存储(纯文本),而是使用带盐的哈希。
今天,这不仅是一种最佳实践,实际上,您必须根据GDPR标识符和类似法规依法进行。
其他回答
使用下面的代码,您可以以字符串形式获取Android OS设备的唯一设备ID。
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_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
大多数Build成员都是字符串,我们在这里所做的是获取它们的长度,并通过数字中的模来转换它。我们有13个这样的数字,我们在前面增加了两个(35),以具有与IMEI相同的大小ID(15个数字)。这里还有其他的可能性,好吧,看看这些字符串。返回355715565309247。无需特别许可,这使得该方法非常方便。
(额外信息:上面给出的技术是从Pocket Magic上的一篇文章中复制的。)
#上次更新时间:2015年6月2日
在阅读了每一篇StackOverflow关于创建唯一ID的帖子、Google开发者博客和Android文档后,我觉得“伪ID”似乎是最好的选择。
主要问题:硬件与软件
硬件
用户可以改变他们的硬件、Android平板电脑或手机,因此基于硬件的唯一ID对于追踪用户来说不是好主意对于跟踪硬件,这是一个好主意
软件
用户可以擦除/更改他们的ROM,如果他们是根用户您可以跨平台(iOS、Android、Windows和Web)跟踪用户在个人用户同意的情况下跟踪他们的最佳方式是让他们登录(使用OAuth实现无缝)
#Android系统的总体故障
###-保证API的唯一性(包括根设备)>=9/10(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 the unique ID of build information (may overlap data - API < 9)
感谢@stansult发布我们的所有选项(在堆栈溢出问题中)。
##选项列表-为什么/为什么不使用它们:
用户电子邮件-软件用户可以更改电子邮件-极不可能API 5+<使用权限android:name=“android.permission.GET_ACCOUNTS”/>或API 14+<uses permission android:name=“android.ppermission.READ_PROFILE”/><uses权限android:nname=“android.prpermission.RREAD_CONTACTS”/>(如何获取android设备的主电子邮件地址)用户电话号码-软件用户可以更改电话号码-极不可能<uses permission android:name=“android.ppermission.READ_PHONE_STATE”/>IMEI-硬件(仅限手机,需要android。permission.READ_PHONE_STATE)大多数用户讨厌在权限中显示“电话呼叫”。一些用户给出了糟糕的评级,因为他们认为你只是在窃取他们的个人信息,而你真正想做的只是跟踪设备安装。很明显,您正在收集数据。<uses permission android:name=“android.ppermission.READ_PHONE_STATE”/>Android ID-硬件(可以为空,可以在工厂重置时更改,可以在根设备上更改)由于它可以是“null”,我们可以检查“null”并更改其值,但这意味着它将不再是唯一的。如果您的用户具有出厂重置设备,则根设备上的值可能已更改或更改,因此如果您正在跟踪用户安装,则可能存在重复条目。WLAN MAC地址-硬件(需要android.permission.ACCESS_WIFI_STATE)这可能是第二个最佳选项,但您仍在收集和存储直接来自用户的唯一标识符。很明显,您正在收集数据。<uses permission android:name=“android.permission.ACCESS_WIFI_STATE”/>蓝牙MAC地址-硬件(带蓝牙的设备,需要android.permission.蓝牙)市场上的大多数应用程序都不使用蓝牙,因此如果您的应用程序不使用蓝牙并且您包含了这一功能,用户可能会变得可疑。<uses permission android:name=“android.permission.BLUETOOTH”/>伪唯一ID-软件(适用于所有Android设备)很有可能,可能包含冲突-请参阅下面发布的我的方法!这允许您从用户那里获得一个“几乎唯一”的ID,而无需获取任何私人信息。您可以根据设备信息创建自己的匿名ID。
我知道没有任何“完美”的方法可以在不使用权限的情况下获得唯一的ID;然而,有时我们只需要跟踪设备安装。在创建唯一ID时,我们可以仅基于Android API提供的信息创建“伪唯一ID”,而无需使用额外的权限。通过这种方式,我们可以表现出对用户的尊重,并尝试提供良好的用户体验。
使用伪唯一id,您实际上只会遇到这样一个事实:基于存在类似设备的事实,可能存在重复项。您可以调整组合方法,使其更独特;然而,一些开发人员需要跟踪设备的安装情况,这将根据类似的设备来完成技巧或性能。
##API>=9:
如果他们的Android设备是API 9或更高版本,则由于“Build.SSERIAL”字段,这是唯一的。
记住,从技术上讲,你只错过了大约0.5%的API<9的用户。所以你可以专注于剩下的:这是99.5%的用户!
##API<9:
如果用户的Android设备低于API 9;希望他们没有进行工厂重置,他们的“Secure.ANDROID_ID”将被保留或不为“null”。(参见http://developer.android.com/about/dashboards/index.html)
##如果所有其他操作都失败:
如果所有其他操作都失败了,如果用户的API低于API 9(低于姜饼),重置了设备,或者“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开发者控制台:
从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.htmlhttp://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
##重要信息:
广告ID旨在完全取代现有的为广告目的使用其他标识符(例如使用ANDROID_ID在Settings.Secure中)。案例Google Play服务不可用的位置由引发的GooglePlayServicesNotAvailableException获取广告IdInfo()。
##警告,用户可以重置:
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
我试图引用我从中获取信息的每个链接。如果你失踪了,需要加入,请评论!
Google Player服务实例ID
https://developers.google.com/instance-id/
当设备没有电话功能时,我使用以下代码获取IMEI或使用Secure.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);
为了完整起见,以下是如何在Xamarin.Android和C#中获取Id:
var id = Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId);
或者如果您不在“活动”中:
var id = Settings.Secure.GetString(context.ContentResolver, Settings.Secure.AndroidId);
其中上下文是传入的上下文。