Android设备有唯一的ID吗?如果有,使用Java访问它的简单方法是什么?


当前回答

String SERIAL_NUMER = Build.SERIAL;

以每个设备中唯一的字符串形式返回SERIAL NUMBER。

其他回答

这是一个简单的问题,没有简单的答案。

此外,这里所有现有的答案要么过时,要么不可靠。

因此,如果您正在寻找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设备的硬件识别,您可以检查MAC地址。

你可以这样做:

在AndroidManifest.xml中

<uses permission android:name=“android.ppermission.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,此代码也能正常工作。

附笔。它们是一堆其他接口,你可以从包含MACS的列表中获得,但这可以在手机之间改变。

这里有30多个答案,有些是相同的,有些是独特的。这个答案是基于这些答案中的一些。其中一个是Lenn Dolling的回答。

它组合3个ID并创建32位十六进制字符串。这对我来说效果很好。

3个ID为:伪ID-根据物理设备规范生成ANDROID_ID-设置.Securite.ANDROID-ID蓝牙地址-蓝牙适配器地址

它将返回如下内容:551F27C060712A72730B0F734064B1

注意:您始终可以向longId字符串添加更多ID。例如,序列号。wifi适配器地址。伊梅。通过这种方式,您可以使每个设备都更独特。

@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 "";
}

只是提醒大家阅读更多最新信息。对于Android O,系统管理这些ID的方式有一些变化。

https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html

tl;dr Serial将需要PHONE权限,Android ID将根据不同的应用程序的包名和签名进行更改。

此外,谷歌还编制了一份很好的文档,提供了有关何时使用硬件和软件ID的建议。

https://developer.android.com/training/articles/user-data-ids.html

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

谷歌似乎打算使用这个ID来识别Android、Chrome和iOS的安装。

它标识的是安装而不是设备,但同样,ANDROID_ID(这是公认的答案)现在也不再标识设备。使用ARC运行时,会为每个安装生成一个新的ANDROID_ID(此处详细信息),就像这个新的实例ID一样。此外,我认为识别安装(而不是设备)是我们大多数人真正需要的。

实例ID的优点

在我看来,谷歌打算将其用于此目的(识别您的安装),它是跨平台的,可以用于许多其他目的(请参阅上面的链接)。

如果您使用GCM,那么您最终将需要使用此实例ID,因为您需要它来获取GCM令牌(它取代了旧的GCM注册ID)。

缺点/问题

在当前的实现(GPS 7.5)中,当应用程序请求实例ID时,会从服务器检索实例ID。这意味着上面的调用是一个阻塞调用-在我的非科学测试中,如果设备处于联机状态,则需要1-3秒,如果设备离线,则需要0.5-1.0秒(大概这是在放弃并生成随机ID之前等待的时间)。这是在北美使用Android 5.1.1和GPS 7.5在Nexus 5上测试的。

如果您将ID用于他们想要的目的,例如应用程序认证、应用程序识别、GCM,我认为这1-3秒可能会令人讨厌(当然,这取决于您的应用程序)。