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


当前回答

我要补充一点,我有一种独特的情况。

使用:

deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);

事实证明,即使我的优派G平板电脑报告的DeviceID不为空,但每个G平板电脑都报告相同的数字。

玩“Pocket Empires”很有趣,它可以让您基于“唯一”DeviceID即时访问某人的帐户。

我的设备没有手机收音机。

其他回答

为了完整起见,以下是如何在Xamarin.Android和C#中获取Id:

var id = Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId);

或者如果您不在“活动”中:

var id = Settings.Secure.GetString(context.ContentResolver, Settings.Secure.AndroidId);

其中上下文是传入的上下文。

IMEI怎么样。这对于Android或其他移动设备来说是独一无二的。

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

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

因此,如果您正在寻找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标识符和类似法规依法进行。

API级别9(Android 2.3-姜饼)中的Build类中添加了一个Serial字段。文档表示它代表硬件序列号。因此,如果设备上存在,它应该是唯一的。

我不知道API级别>=9的所有设备是否都支持(=不为空)。

我的两美分-注意,这是一个设备(错误)唯一ID,而不是Android开发者博客中讨论的安装ID。

值得注意的是,@emmby提供的解决方案在每个应用程序ID中都有所不同,因为SharedPreferences没有跨进程同步(请参阅此处和此处)。所以我完全避免了这一点。

相反,我封装了在枚举中获取(设备)ID的各种策略-更改枚举常量的顺序会影响获取ID的各种方式的优先级。返回第一个非空ID或抛出异常(根据不赋予空含义的良好Java实践)。例如,我先有一个TELEPHONY,但一个好的默认选择是ANDROID_ID贝塔:

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);
        }
    }
}