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


当前回答

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

完整披露:我的应用程序最初使用了以下方法,但现在不再使用这种方法,我们现在使用了emmby的答案链接所指向的Android开发者博客条目中概述的方法(即,生成并保存UUID#randomUUID())。


这个问题有很多答案,其中大多数只在“某些”时间有效,不幸的是,这还不够好。

根据我对设备的测试(所有手机,至少有一部未激活):

所有测试的设备都返回了TelephonyManager.getDeviceId()的值所有GSM设备(均使用SIM进行测试)都返回了TelephonyManager.getSimSerialNumber()的值对于getSimSerialNumber(),所有CDMA设备均返回null(如预期)添加了Google帐户的所有设备都返回了ANDROID_ID的值所有CDMA设备都为ANDROID_ID和TelephonyManager.getDeviceId()返回相同的值(或相同值的派生)——只要在安装过程中添加了Google帐户。我还没有机会测试没有SIM卡的GSM设备,没有添加谷歌账户的GSM设备或任何处于飞行模式的设备。

因此,如果您想要设备本身独有的东西,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();

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

这对我来说足够好了。

正如Richard在下面提到的那样,不要忘记您需要权限才能读取TelephonyManager财产,因此请将此添加到您的清单中:

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

导入库

import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;

其他回答

TelephonyManager.getDeviceId()返回唯一的设备ID,例如,GSM的IMEI和CDMA电话的MEID或ESN。

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

但我建议使用:

Settings.Secure.ANDROID_ID,将ANDROID ID作为唯一的64位十六进制字符串返回。

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

有时TelephonyManager.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;
}

#上次更新时间: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/

这里有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 OS设备的唯一设备ID。

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

只是提醒大家阅读更多最新信息。对于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