关于uniqueidentifier:是否有唯一的Android设备ID?

Is there a unique Android device ID?

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


Settings.Secure#ANDROID_ID返回android id作为每个用户64位十六进制字符串的唯一值。

1
2
3
4
import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID);


更新:在最新版本的Android中,许多与ANDROID_ID有关的问题已经得到解决,我相信这种方法不再是必要的。请看一下安东尼的回答。

完全公开:我的应用最初使用下面的方法,但不再使用这种方法,现在我们使用的方法概述在Android开发者博客条目中,Emmby的答案链接(即,生成和保存一个UUID#randomUUID())。

这个问题有很多答案,大多数答案在某些时候只起作用,不幸的是,这还不够好。

根据我对设备的测试(所有电话,其中至少有一个未激活):

  • 测试的所有设备都返回了TelephonyManager.getDeviceId()的值。
  • 所有GSM设备(均用SIM测试)返回TelephonyManager.getSimSerialNumber()的值。
  • 所有CDMA设备返回的getSimSerialNumber()为空(如预期)
  • 所有添加了google账户的设备都返回了ANDROID_ID的值。
  • 只要在设置期间添加了Google帐户,所有CDMA设备都会为ANDROID_IDTelephonyManager.getDeviceId()返回相同的值(或相同值的派生)。
  • 我还没有机会在没有SIM卡的情况下测试GSM设备,在没有谷歌帐户的情况下测试GSM设备,或者在飞行模式下测试任何设备。
  • 因此,如果您想要设备本身特有的东西,那么TM.getDeviceId()就足够了。显然,有些用户比其他用户更偏执,因此散列这些标识符中的1个或多个可能很有用,这样字符串对设备仍然是唯一的,但不会显式地标识用户的实际设备。例如,使用String.hashCode()与uuid结合使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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();

    可能导致类似的结果:00000000-54b3-e7c7-0000-000046bffd97

    它对我来说已经足够好了。

    正如Richard在下面提到的,不要忘记您需要许可才能读取TelephonyManager属性,因此将其添加到您的清单中:

    1
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    导入库

    1
    2
    3
    import android.content.Context;
    import android.telephony.TelephonyManager;
    import android.view.View;


    上次更新时间:6/2/15

    在阅读了每一篇关于创建唯一ID、Google开发者博客和Android文档的堆栈溢出文章之后,我觉得"伪ID"是最好的选择。好的。主要问题:硬件与软件硬件

    • 用户可以更改他们的硬件,Android平板电脑或手机,因此基于硬件的独特ID对于跟踪用户来说不是好主意。
    • 对于跟踪硬件,这是一个好主意

    软件

    • 如果用户是根用户,则可以擦除/更改其ROM
    • 您可以跨平台(iOS、Android、Windows和Web)跟踪用户
    • 在征得个人同意的情况下,最好是让他们登录(使用OAuth实现无缝连接)。

    安卓系统整体故障-保证API的唯一性(包括根设备)>>9/10(99.5%的Android设备)-没有额外权限

    伪代码:好的。

    1
    2
    3
    4
    5
    6
    7
    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+
      • api 14+(如何获取Android设备的主要电子邮件地址)
    • 用户电话号码-软件好的。

      • 用户可以更改电话号码-极不可能
    • IMEI-硬件(仅限电话,需要android.permission.READ_PHONE_STATE)好的。

      • 大多数用户都不喜欢它在权限中说"电话"。有些用户给出的评价很差,因为他们认为你只是在窃取他们的个人信息,而你真正想做的只是跟踪设备的安装。很明显,您正在收集数据。
    • Android ID-硬件(可以为空,可以在工厂重置时更改,可以在根设备上更改)好的。

      • 因为它可以是"空",我们可以检查"空"并更改其值,但这意味着它将不再是唯一的。
      • 如果用户具有出厂重置设备,则根设备上的值可能已更改或更改,因此在跟踪用户安装时可能存在重复条目。
    • WLAN MAC地址-硬件(需要android.permission.ACCESS_WIFI_STATE)好的。

      • 这可能是第二个最好的选择,但您仍然在收集和存储直接来自用户的唯一标识符。很明显,您正在收集数据。
    • 蓝牙MAC地址-硬件(带蓝牙的设备,需要android.permission.BLUETOOTH)好的。

      • 市场上的大多数应用程序都不使用蓝牙,因此,如果您的应用程序不使用蓝牙,并且包括蓝牙,用户可能会变得可疑。
    • 伪唯一ID-软件(适用于所有Android设备)好的。

      • 很有可能,可能包含碰撞-请参阅下面发布的我的方法!
      • 这允许您从用户那里获得一个"几乎唯一"的ID,而不占用任何私有的内容。您可以根据设备信息创建自己的匿名ID。

    我知道没有任何"完美"的方法可以在不使用权限的情况下获得唯一的ID;但是,有时我们只需要跟踪设备安装。在创建唯一的ID时,我们可以创建一个"伪唯一ID",它完全基于Android API提供的信息,而不需要使用额外的权限。这样,我们就可以表现出对用户的尊重,并尝试提供良好的用户体验。好的。

    使用伪唯一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(低于姜饼),重置了他们的设备或'secure.android'id'返回'null',那么返回的ID将完全基于他们的android设备信息。这就是发生碰撞的地方。好的。

    变化:好的。

    • 由于工厂重置,删除了"android.secure_id",可能导致值更改
    • 已编辑要在API上更改的代码
    • 更改了伪

    请看下面的方法:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    /**
     * 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开发者的控制台:好的。

    Beginning August 1st, 2014, the Google Play Developer Program Policy
    requires all new app uploads and updates to use the advertising ID in
    lieu of any other persistent identifiers for any advertising purposes.
    Learn more

    Ok.

    实施:好的。

    许可:好的。

    1
    <uses-permission android:name="android.permission.INTERNET" />

    代码:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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/advertisingclient.html好的。重要:

    It is intended that the advertising ID completely replace existing
    usage of other identifiers for ads purposes (such as the use of ANDROID_ID
    in Settings.Secure) when Google Play Services is available. Cases
    where Google Play Services is unavailable are indicated by a
    GooglePlayServicesNotAvailableException being thrown by
    getAdvertisingIdInfo().

    Ok.

    警告,用户可以重置:

    http://en.kioskea.net/faq/34732-android-reset-your-advertising-id好的。

    我试图引用我从中获取信息的每个链接。如果您不在,需要包括在内,请评论!好的。谷歌播放器服务实例ID

    https://developers.google.com/instance-id/好的。好啊。


    正如DaveWebb所提到的,Android开发者博客上有一篇文章介绍了这一点。他们的首选解决方案是跟踪应用程序的安装,而不是设备,这对于大多数使用案例都很有效。博客文章将向您展示使其工作所必需的代码,我建议您检查一下。

    但是,如果您需要设备标识符而不是应用程序安装标识符,博客文章将继续讨论解决方案。我和谷歌的某个人谈过,如果你需要的话,我会对一些项目做进一步的澄清。以下是我在上述博客文章中没有提到的设备标识符的发现:

    • android_id是首选设备标识符。android_id在android<=2.1或>=2.3版本上非常可靠。只有2.2存在岗位提到的问题。
    • 多家制造商生产的多个设备受2.2中的Android_id错误的影响。
    • 据我所知,所有受影响的设备都具有相同的安卓ID,即9774D56D682E549C,这也是仿真器btw报告的相同设备ID。
    • 谷歌认为,原始设备制造商已经解决了许多或大多数设备的问题,但我能够证实,至少在2011年4月初,找到安卓ID已损坏的设备仍然相当容易。

    根据Google的建议,我实现了一个类,它将为每个设备生成一个唯一的UUID,在适当的情况下使用android作为种子,必要时返回telephonyManager.getdeviceID(),如果失败,则使用随机生成的唯一UUID,该UUID在应用程序重新启动(但不是应用程序重新安装)期间保持不变。

    请注意,对于必须回退设备ID的设备,唯一ID将在工厂重置期间保持不变。这是值得注意的事情。如果您需要确保恢复出厂设置将重置您的唯一ID,您可能需要考虑直接回退到随机UUID,而不是设备ID。

    同样,此代码用于设备ID,而不是应用程序安装ID。在大多数情况下,应用程序安装ID可能是您要查找的。但是如果您确实需要设备ID,那么下面的代码可能对您有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    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;
        }
    }


    下面是Reto Meier在今年的Google I/O演示中为用户获取唯一ID所使用的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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的谈话中描述),那么您应该拥有一个与用户关联的ID,并在设备被擦除或更换后一直保留。我计划在未来的分析中使用它(换句话说,我还没有做那一点)。


    您也可以考虑Wi-Fi适配器的MAC地址。检索时间:

    1
    2
    WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
    return wm.getConnectionInfo().getMacAddress();

    需要舱单中的android.permission.ACCESS_WIFI_STATE许可。

    报告即使未连接Wi-Fi也可用。如果上面答案的乔在他的许多设备上尝试一下,那就太好了。

    在某些设备上,关闭Wi-Fi时无法使用。

    注:从android 6.x返回一致的假MAC地址:02:00:00:00:00:00


    这里有相当有用的信息。

    它包括五种不同的ID类型:

  • IMEI(仅适用于使用手机的Android设备;需要android.permission.READ_PHONE_STATE)
  • 伪唯一ID(适用于所有Android设备)
  • Android ID(可以为空,可以在出厂重置时更改,可以在根电话上更改)
  • WLAN MAC地址字符串(需要android.permission.ACCESS_WIFI_STATE)
  • bt-mac地址字符串(带蓝牙的设备,需要android.permission.BLUETOOTH)

  • 官方的Android开发者博客现在有一篇关于这个主题的完整文章,介绍了应用程序的安装。


    在Google I/O上,Reto Meier发布了一个强大的解决方案,它可以满足大多数开发人员跟踪用户安装的需求。安东尼·诺兰在他的回答中指明了方向,但我想我应该写下完整的方法,这样其他人就可以很容易地看到怎么做了(我花了一段时间才弄清楚细节)。

    这种方法将为您提供一个匿名的、安全的用户ID,它将在不同的设备(基于主Google帐户)和不同的安装之间为用户提供持久性。基本方法是生成一个随机的用户ID,并将其存储在应用程序的共享首选项中。然后使用Google的备份代理将链接到Google帐户的共享首选项存储在云中。

    让我们来看看完整的方法。首先,我们需要使用Android备份服务为共享的引用创建备份。首先通过http://developer.android.com/google/backup/signup.html注册应用程序。

    谷歌会给你一个备份服务密钥,你需要添加到清单中。您还需要告诉应用程序使用backupagent,如下所示:

    1
    2
    3
    4
    5
    6
    <application android:label="MyApplication"
             android:backupAgent="MyBackupAgent">
        ...
        <meta-data android:name="com.google.android.backup.api_key"
            android:value="your_backup_service_key" />
    </application>

    然后,您需要创建备份代理并告诉它使用用于共享引用的助手代理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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);
        }
    }

    要完成备份,您需要在主活动中创建backupManager的实例:

    1
    BackupManager backupManager = new BackupManager(context);

    最后,如果用户ID尚不存在,请创建它,并将其存储在sharedreferences中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
      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;
    }

    即使用户移动设备,此用户ID现在也将在安装过程中保持不变。

    有关此方法的更多信息,请参阅reto的谈话。

    有关如何实现备份代理的完整详细信息,请参阅数据备份。我特别推荐下面关于测试的部分,因为备份不会立即发生,所以为了测试,您必须强制执行备份。


    以下代码使用隐藏的Android API返回设备序列号。但是,这个代码在三星Galaxy标签上不起作用,因为这个设备上没有设置"ro.serialno"。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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) {

    }


    我想这肯定是构建一个独特身份的骨架的方法…过来看。

    伪唯一ID,适用于所有Android设备有些设备没有电话(如平板电脑),或者出于某种原因,您不希望包含"读取电话"状态许可。您仍然可以读取详细信息,如ROM版本、制造商名称、CPU类型和其他硬件详细信息,如果您想将ID用于串行密钥检查或其他一般用途,这些信息将非常适合。以这种方式计算的ID不是唯一的:可以找到两个具有相同ID的设备(基于相同的硬件和ROM映像),但现实应用程序中的变化可以忽略不计。为此,您可以使用build类:

    1
    2
    3
    4
    5
    6
    7
    8
    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相同的大小ID(15个数字)。这里还有其他的可能性,好吧,看看这些字符串。返回类似于355715565309247的值。不需要特别许可,这使得这种方法非常方便。

    (额外信息:上面给出的技术是从一篇关于口袋魔法的文章中复制出来的。)


    使用下面的代码,您可以获得作为字符串的Android OS设备的唯一设备ID。

    1
    deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);

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

    不过,我不知道API级别大于等于9的所有设备是否都支持它(=非空)。


    我要补充一件事——我有一个独特的情况。

    使用:

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

    结果表明,尽管我的ViewSonic G Tablet报告的设备ID不是空的,但每个G Tablet报告的数字都相同。

    玩"Pocket Empires"很有趣,它可以让你根据"唯一"设备ID即时访问某人的帐户。

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


    有关如何为安装应用程序的每个Android设备获取唯一标识符的详细说明,请参阅官方Android开发者博客中的"标识应用程序安装"。

    似乎最好的方法是在安装时自己生成一个,然后在重新启动应用程序时读取它。

    我个人认为这是可以接受的,但并不理想。Android提供的任何一个标识符在所有情况下都不起作用,因为大多数标识符取决于手机的无线电状态(Wi-Fi开/关、手机开/关、蓝牙开/关)。其他的,如Settings.Secure.ANDROID_ID必须由制造商实施,不能保证是唯一的。

    以下是将数据写入安装文件的示例,安装文件将与应用程序本地保存的任何其他数据一起存储。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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();
        }
    }


    在类文件中添加以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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);

    加载项androidmanifest.xml:

    1
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    一个独特的设备ID的字符串使用的Android操作系统的设备,是由TelephonyManagerANDROID_ID,得到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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);
    }

    但我强烈建议的方法的应用程序的建议的谷歌的,国有企业的识别装置。


    有很多不同的方法可以解决这些ANDROID_ID问题(有时可能是null问题,或者特定型号的设备总是返回相同的ID),有利弊:

    • 实现自定义ID生成算法(基于设备属性,这些属性应该是静态的,不会更改->谁知道)
    • 滥用其他ID,如IMEI、序列号、Wi-Fi/蓝牙MAC地址(它们不会存在于所有设备上,或者需要额外的权限)

    我自己更喜欢使用现有的OpenUDID实现(见https://github.com/ylechelle/openudid)来实现Android(见https://github.com/vieux/openudid)。很容易整合和利用ANDROID_ID和上述问题的回退。


    我的2美分-注意这是一个设备(err)唯一的ID-而不是安卓开发者博客中讨论的安装ID。

    请注意,@emmby提供的解决方案返回到每个应用程序ID中,因为共享的引用不是跨进程同步的(请参见此处和此处)。所以我完全避免了。

    相反,我封装了在枚举中获取(设备)ID的各种策略。更改枚举常量的顺序会影响获得ID的各种方式的优先级。返回第一个非空ID或抛出异常(按照不给出空含义的好的Java实践)。例如,我首先拥有电话服务,但一个好的默认选择是Android_ID贝塔:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    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);
            }
        }
    }

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


    这里有30多个答案,有些是相同的,有些是独特的。这个答案是基于这些答案中的很少一个。其中一个是@lenn dolling的答案。

    它组合了3个ID并创建了一个32位的十六进制字符串。它对我很管用。

    3个ID是:伪ID-根据物理设备规范生成安卓-Settings.Secure.ANDROID_ID蓝牙地址-蓝牙适配器地址

    它将返回如下内容:551F27C060712A72730B0A0F734064B1

    注意:您总是可以向longId字符串添加更多的ID。例如,串行。WiFi适配器地址。IMEI。这样,您就可以使每个设备更独特。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    @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"";
    }


    以下是生成唯一ID的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;
    }


    更具体而言,Settings.Secure.ANDROID_ID。这是一个64位的生成和存储量是当装置第一靴。当它是wiped复位装置。

    ANDROID_ID似乎是很好的选择,独特的设备标识符。有缺点:第一,它是不是100%可靠的在线版的Android 2.2,也("Froyo").之前,至少有一个已被广泛观察到的缺陷在受欢迎的手机-从专业制造商,在每一个实例具有相同的ID _ Android。


    在Android手机的硬件识别的特异性,可以检查你的MAC地址。

    你可以这样做:

    在大纲

    现在在你的代码:

    1
    2
    3
    4
    5
    6
    List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());

    for (NetworkInterface interface : interfacesList) {
       // This will give you the interface MAC ADDRESS
       interface.getHardwareAddress();
    }

    他们是在每一个机器人设备至少是"wlan0"女巫的Wi - Fi的接口芯片。此代码工作在Wi - Fi的,甚至是没有打开。

    附注:他们是一帮你将得到来自其他接口的列表,但这种变化之间的CAN含苹果手机。


    当设备没有电话功能时,我使用以下代码获取IMEI或使用secure.ANDROID_ID作为备选方案:

    1
    2
    3
    4
    5
    6
    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);

    另一种方法是在没有任何许可的应用程序中使用/sys/class/android_usb/android0/iSerial

    1
    2
    3
    4
    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

    要在Java中实现这一点,可以使用FieldPixStand打开ISLID文件并读出字符。请确保将其包装在异常处理程序中,因为并非所有设备都有此文件。

    已知至少以下设备具有此文件世界可读性:

    • 银河系
    • 联结S
    • 摩托罗拉Xoom 3G
    • 东芝AT300
    • 宏达一V
    • 迷你MK802
    • 三星Galaxy S II

    你也可以看到我的博客文章将Android硬件序列号泄露给了非特权应用程序,在这里我讨论了其他哪些文件可以获取信息。


    telephonymanger.getdeviceid()返回设备的唯一ID码,例如,在GSM和CDMA手机的ESN或MEID。

    1
    2
    final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);            
    String myAndroidDeviceId = mTelephony.getDeviceId();

    但我建议使用:

    settings.secure.android _ ID是返回的唯一id的一个64位的十六进制字符串。

    1
        String   myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);

    有时telephonymanger.getdeviceid()将返回空,所以来向你将利用这一独特的ID的方法是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;
    }


    谷歌实例ID

    在2015年之前发布的Android上的I / O的即插即用服务要求;7.5。

    https:////developers.google.com实例IDdevelopers.google.com https://///android实例标识实施指南

    1
    2
    InstanceID iid = InstanceID.getInstance( context );   // Google docs are wrong - this requires context
    String id = iid.getId();  // blocking call

    看来,谷歌打算这个ID是用来识别设备在iOS和Android,Chrome。

    它的发现,而借助一安装装置,然后再_,Android的ID(这是现在明确接受的答案的任何设备的时间。新的Android运行时的电弧产生的每一个安装在一_ ID(详情在这里),这就像新的实例ID。还有,我认为(不确定是什么样的设备)的设备大部分是真的为寻找。

    在优势的实例ID

    它的出现对我说,谷歌打算在它被用于这种用途的识别你的设备),它是跨平台的,可以被用于一些其他用途的(见上面的链接)。

    如果你使用),然后你会需要使用这个实例ID eventually因为你需要它为了在大气环流模式(GCM replaces令牌,旧注册的ID)。

    在disadvantages /问题

    在目前的实现GPS(7.5)retrieved实例ID是从服务器请求时,您的应用程序就可以了。这意味在一个呼叫阻塞调用-在我的unscientific测试需要1—3秒,如果设备是0.5~1.0秒,在线和离线(如果这是一presumably等待多久之前放弃它与随机生成的ID)。这是在北美洲的在线测试与Android手机Nexus 5.1.1 GPS 5和7.5。

    如果你使用ID的目的,他们是打算如APP。APP模式的识别,认证,我认为这可能是1~3秒(这取决于你的应用程序在弄,当然)。


    android设备mac id也是一个唯一的id,它不会改变假设如果我们格式化设备本身,使用以下代码获取mac id

    1
    2
    3
    WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    WifiInfo info = manager.getConnectionInfo();
    String address = info.getMacAddress();

    也不要忘记将适当的权限添加到androidmanifest.xml文件

    1
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>


    1.您可以通过GMS服务访问Android设备ID。见下例:

    1
    2
    3
    private DeviceInfoProvider mDeviceInfo = new DeviceInfoProvider(Context)
    String mDeviceId = DeviceInfoProvider.getDeviceId(Context);
    Log.d("DEVICE_ID" , mDeviceId);

    2.使用电话管理器,它提供唯一的ID(即IMEI)。参见示例,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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();

    这需要android.permission.read_phone_状态给你的用户,这很难证明你所做的应用类型是正确的。

  • 没有电话服务的设备(如平板电脑)必须报告一个唯一的设备ID,该ID可以通过android.os.build.serial从android 2.3姜饼中获得。有些具有电话服务的电话也可以定义序列号。就像不是所有的Android设备都有序列号一样,这个解决方案也不可靠。

  • 在设备首次引导时,会生成并存储一个随机值。此值可通过settings.secure.android_id获得。它是一个64位数字,在设备的生命周期内应该保持不变。Android_ID似乎是唯一设备标识符的好选择,因为它可用于智能手机和平板电脑。要检索该值,可以使用以下代码,

    string androidid=settings.secure.getString(getContentResolver(),settings.secure.android_id);

  • 但是,如果在设备上执行出厂重置,该值可能会更改。还有一个已知的bug,来自一个制造商的流行手机,每个实例都有相同的android_id。显然,解决方案不是100%可靠。

  • 使用UUID。由于大多数应用程序的需求是标识特定的安装,而不是物理设备,因此,如果要使用UUID类,则可以获得用户的唯一ID。以下解决方案由谷歌的reto meier在谷歌I/O演示文稿中介绍,
  • SharedPreferences sharedPrefs = context.getSharedPreferences(
    PREF_UNIQUE_ID, Context.MODE_PRIVATE);
    uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);


    谷歌现在有了一个广告ID。这也可以使用,但请注意:

    The advertising ID is a user-specific, unique, resettable ID

    enables users to reset their identifier or opt out of interest-based ads within Google Play apps.

    因此,尽管这个ID可能会改变,但我们似乎很快就没有选择了,这取决于这个ID的用途。

    更多信息@develper.android

    复制粘贴代码到此处

    高温高压


    不建议将deviceid用作第三方的跟踪,但这是另一种方式。

    1
    2
    3
    4
    5
    6
    @SuppressLint("HardwareIds")
    private String getDeviceID() {
        deviceId = Settings.Secure.getString(getApplicationContext().getContentResolver(),
                        Settings.Secure.ANDROID_ID);
        return deviceId;
    }


    几年前,我遇到了这个问题,并且学会了基于各种答案实现一个通用的解决方案。

    我已经在一个实际产品中使用了几年的广义解。到目前为止,它对我很有帮助。下面是基于各种提供的答案的代码片段。

    注意,getEmail大多数时候会返回空值,因为我们没有明确要求许可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    private static UniqueId getUniqueId() {
        MyApplication app = MyApplication.instance();

        // Our prefered method of obtaining unique id in the following order.
        // (1) Advertising id
        // (2) Email
        // (2) ANDROID_ID
        // (3) Instance ID - new id value, when reinstall the app.

        ////////////////////////////////////////////////////////////////////////////////////////////
        // ADVERTISING ID
        ////////////////////////////////////////////////////////////////////////////////////////////
        AdvertisingIdClient.Info adInfo = null;
        try {
            adInfo = AdvertisingIdClient.getAdvertisingIdInfo(app);
        } catch (IOException e) {
            Log.e(TAG,"", e);
        } catch (GooglePlayServicesNotAvailableException e) {
            Log.e(TAG,"", e);
        } catch (GooglePlayServicesRepairableException e) {
            Log.e(TAG,"", e);
        }

        if (adInfo != null) {
            String aid = adInfo.getId();
            if (!Utils.isNullOrEmpty(aid)) {
                return UniqueId.newInstance(aid, UniqueId.Type.aid);
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // EMAIL
        ////////////////////////////////////////////////////////////////////////////////////////////
        final String email = Utils.getEmail();
        if (!Utils.isNullOrEmpty(email)) {
            return UniqueId.newInstance(email, UniqueId.Type.eid);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // ANDROID ID
        ////////////////////////////////////////////////////////////////////////////////////////////
        final String sid = Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID);
        if (!Utils.isNullOrEmpty(sid)) {
            return UniqueId.newInstance(sid, UniqueId.Type.sid);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // INSTANCE ID
        ////////////////////////////////////////////////////////////////////////////////////////////
        final String iid = com.google.android.gms.iid.InstanceID.getInstance(MyApplication.instance()).getId();
        if (!Utils.isNullOrEmpty(iid)) {
            return UniqueId.newInstance(iid, UniqueId.Type.iid);
        }

        return null;
    }

    public final class UniqueId implements Parcelable {
        public enum Type implements Parcelable {
            aid,
            sid,
            iid,
            eid;

            ////////////////////////////////////////////////////////////////////////////
            // Handling Parcelable nicely.

            public static final Parcelable.Creator<Type> CREATOR = new Parcelable.Creator<Type>() {
                public Type createFromParcel(Parcel in) {
                    return Type.valueOf(in.readString());
                }

                public Type[] newArray(int size) {
                    return new Type[size];
                }
            };

            @Override
            public int describeContents() {
                return 0;
            }

            @Override
            public void writeToParcel(Parcel parcel, int flags) {
                parcel.writeString(this.name());
            }

            // Handling Parcelable nicely.
            ////////////////////////////////////////////////////////////////////////////
        }

        public static boolean isValid(UniqueId uniqueId) {
            if (uniqueId == null) {
                return false;
            }
            return uniqueId.isValid();
        }

        private boolean isValid() {
            return !org.yccheok.jstock.gui.Utils.isNullOrEmpty(id) && type != null;
        }

        private UniqueId(String id, Type type) {
            if (org.yccheok.jstock.gui.Utils.isNullOrEmpty(id) || type == null) {
                throw new java.lang.IllegalArgumentException();
            }
            this.id = id;
            this.type = type;
        }

        public static UniqueId newInstance(String id, Type type) {
            return new UniqueId(id, type);
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = 31 * result + id.hashCode();
            result = 31 * result + type.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }

            if (!(o instanceof UniqueId)) {
                return false;
            }

            UniqueId uniqueId = (UniqueId)o;
            return this.id.equals(uniqueId.id) && this.type == uniqueId.type;
        }

        @Override
        public String toString() {
            return type +":" + id;
        }

        ////////////////////////////////////////////////////////////////////////////
        // Handling Parcelable nicely.

        public static final Parcelable.Creator<UniqueId> CREATOR = new Parcelable.Creator<UniqueId>() {
            public UniqueId createFromParcel(Parcel in) {
                return new UniqueId(in);
            }

            public UniqueId[] newArray(int size) {
                return new UniqueId[size];
            }
        };

        private UniqueId(Parcel in) {
            this.id = in.readString();
            this.type = in.readParcelable(Type.class.getClassLoader());
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel parcel, int flags) {
            parcel.writeString(this.id);
            parcel.writeParcelable(this.type, 0);
        }

        // Handling Parcelable nicely.
        ////////////////////////////////////////////////////////////////////////////

        public final String id;
        public final Type type;
    }

    public static String getEmail() {
        Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
        AccountManager accountManager = AccountManager.get(MyApplication.instance());
        Account[] accounts = accountManager.getAccountsByType("com.google");
        for (Account account : accounts) {
            if (emailPattern.matcher(account.name).matches()) {
                String possibleEmail = account.name;
                return possibleEmail;
            }
        }

        accounts = accountManager.getAccounts();
        for (Account account : accounts) {
            if (emailPattern.matcher(account.name).matches()) {
                String possibleEmail = account.name;
                return possibleEmail;
            }
        }

        return null;
    }

    只是一个提醒每个人阅读寻找更多的最新信息。对于android o,系统管理这些ID的方式有一些变化。

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

    tl;dr serial需要手机许可,不同应用程序的android id将根据其包名和签名进行更改。

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

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


    序列号是通过android.os.build.serial提供的唯一设备ID。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static String getSerial() {
        String serial ="";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            serial = Build.getSerial();
        }else{
            serial = Build.SERIAL;    
        }
        return serial;
    }

    在调用getserial()之前,请确保您已读取了phoneu state权限。

    注意:没有电话的设备(如仅WiFi平板电脑)不能使用它。


    1
    String SERIAL_NUMER = Build.SERIAL;

    以每个设备中唯一的字符串形式返回序列号。


    通常,我的应用程序使用设备唯一ID。但有时我会用IMEI。两者都是唯一的数字。

    获取IMEI(国际移动设备识别码)

    1
    2
    3
    4
    5
    public String getIMEI(Activity activity) {
        TelephonyManager telephonyManager = (TelephonyManager) activity
                .getSystemService(Context.TELEPHONY_SERVICE);
        return telephonyManager.getDeviceId();
    }

    获取设备唯一ID

    1
    2
    3
    4
    5
    public String getDeviceUniqueID(Activity activity){
        String device_unique_id = Secure.getString(activity.getContentResolver(),
                Secure.ANDROID_ID);
        return device_unique_id;
    }

    为了包括Android9,我只有一个想法,那就是(可能)不会违反任何条款,需要权限,并且跨安装和应用程序工作。

    涉及服务器的指纹识别应该能够唯一地识别设备。硬件信息+已安装的应用程序和安装时间的组合应该可以做到这一点。第一次安装时间不会更改,除非重新卸载并安装应用程序。但这必须对设备上的所有应用程序进行,以便无法识别设备(即在工厂重置后)。

    我就是这样想的:

  • 提取硬件信息、应用程序包名称和首次安装时间。
  • 这是从Android中提取所有应用程序的方法(不需要权限):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    final PackageManager pm = application.getPackageManager();
    List<ApplicationInfo> packages =
    pm.getInstalledApplications(PackageManager.GET_META_DATA);

    for (ApplicationInfo packageInfo : packages) {
        try {
            Log.d(TAG,"Installed package :" + packageInfo.packageName);
            Log.d(TAG,"Installed :" + pm.getPackageInfo(packageInfo.packageName, 0).firstInstallTime);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
  • 在将每个包名称和安装时间戳组合发送到服务器之前,您可能需要对其进行散列,因为用户在设备上安装的内容可能与您的业务无关。
  • 有些应用程序(实际上很多)是系统应用程序。它们可能具有相同的安装时间戳,与工厂重置后的最新系统更新相匹配。因为它们具有相同的安装时间戳,所以用户无法安装它们,并且可以将其筛选出来。
  • 将信息发送到服务器,让它在以前存储的信息中查找最近的匹配项。在安装和卸载应用程序时,与以前存储的设备信息进行比较时,需要设置一个阈值。但我的猜测是,这个阈值可能非常低,因为任何包名称和第一次安装时间戳组合对于设备来说都是非常独特的,而且应用程序的安装和卸载也不是那么频繁。拥有多个应用程序只会增加独一无二的可能性。
  • 返回为匹配生成的唯一ID,或生成唯一ID,存储设备信息并返回此新ID。
  • 注意:这是一种未经测试和验证的方法!我相信它会起作用,但我也非常肯定,如果这发生了,他们会以某种方式关闭它。


    为了完整起见,下面介绍如何在Xamarin.Android和c中获取Id

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

    或者如果您不在Activity中:

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

    其中,context是上下文中传递的。


    只获取一次设备ID,然后将其存储在数据库或文件中。在这种情况下,如果它是应用程序的第一次引导,它会生成一个ID并存储它。下次,它将只获取存储在文件中的ID。


    检查是systeminfo.deviceuniqueidentifier

    文档:http:/ / / / / systeminfo-deviceuniqueidentifier.html ScriptReference收集docs.unity3d.com

    独特的设备标识符。它是独特的每一个设备是保证(只读)。

    在线预ios7 iOS设备的MAC地址的哈希值,它将返回美国。在线ios7器件uidevice identifierforvendor或它将失败,如果这是任何原因,asidentifiermanager advertisingidentifier。