Android设备有唯一的ID吗?如果有,使用Java访问它的简单方法是什么?
当前回答
另一种方法是在没有任何权限的应用程序中使用/sys/class/android_usb/android0/iSerial。
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中实现这一点,只需使用FileInputStream打开iSerial文件并读取字符。只需确保将其包装在异常处理程序中,因为并非所有设备都有此文件。
已知至少以下设备具有此文件世界可读性:
银河天枢Nexus S公司摩托罗拉Xoom 3G东芝AT300HTC一V迷你MK802三星Galaxy S II
你也可以看到我的博客文章“泄露Android硬件序列号给未经授权的应用程序”,在这里我讨论了哪些其他文件可供参考。
其他回答
序列号是通过android.os.Build.Serial提供的唯一设备ID。
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()之前,请确保您具有READ_PHONE_STATE权限。
注意:-它不适用于没有电话的设备(如仅支持wifi的平板电脑)。
#上次更新时间: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/
我认为这是建立一个独特身份的骨架的最佳方式……看看吧。
伪唯一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上的一篇文章中复制的。)
我的两美分-注意,这是一个设备(错误)唯一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);
}
}
}
这个示例演示了如何在Android中获取和存储设备ID,但我使用的是Kotlin。
val textView: TextView = findViewById(R.id.textView)
val uniqueId: String = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
textView.text = "Device ID: $uniqueId"
推荐文章
- 警告:API ' variable . getjavacompile()'已过时,已被' variable . getjavacompileprovider()'取代
- 安装APK时出现错误
- 碎片中的onCreateOptionsMenu
- TextView粗体通过XML文件?
- 如何使线性布局的孩子之间的空间?
- DSL元素android.dataBinding。enabled'已过时,已被'android.buildFeatures.dataBinding'取代
- ConstraintLayout:以编程方式更改约束
- PANIC: AVD系统路径损坏。检查ANDROID_SDK_ROOT值
- 如何生成字符串类型的buildConfigField
- Recyclerview不调用onCreateViewHolder
- Android API 21工具栏填充
- Android L中不支持操作栏导航模式
- 如何在TextView中添加一个子弹符号?
- PreferenceManager getDefaultSharedPreferences在Android Q中已弃用
- 在Android Studio中创建aar文件