Android设备是否有唯一的ID,如果有的话,使用Java访问它的简单方法是什么?
我认为这是一个可靠的方法来构建唯一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
大部分生成的成员都是字符串类型,这里我们计算它们的长度并通过取模转换为一个数字。我们有13个这样的数字,然后在前面添加两个(35)以使ID与IMEI(15位数字)具有相同的长度。其它方法也可以实现,请看这些字符串示例。
返回类似于355715565309247
的东西。使用此方法无需特殊权限,非常方便。
(额外信息:上述技术是从Pocket Magic的一篇文章中复制来的。)
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) {
}
使用以下代码,您可以获取Android操作系统设备的唯一设备ID作为字符串。
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
我想要补充一件事 - 我面临的是一种独特的情况。
使用:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
事实证明,即使我的Viewsonic G平板电脑报告的DeviceID不为Null,但每个G平板电脑都报告相同的数字。
这使得玩“口袋帝国”变得有趣,该游戏根据“唯一”的DeviceID,立即让您访问某人的帐户。
我的设备没有蜂窝网络电台。
在API 9级别(Android 2.3 - Gingerbread)的Build
类中添加了一个Serial字段。文档显示它代表硬件序列号,如果存在于设备上应该是唯一的。
然而,我不知道所有API级别大于等于9的设备是否实际支持(不为空值)此字段。
关于如何获取每个安装应用程序的Android设备的唯一标识符的详细说明,请参阅官方的Android Developer博客文章 Identifying App Installations。
看起来最好的方法是在安装时自己生成一个标识符,并在应用程序重新启动时读取它。
我个人认为这样是可接受但并不理想的。Android提供的任何一个标识符都不能在所有情况下正常工作,因为大多数都依赖于手机的无线电状态(Wi-Fi开/关,蜂窝移动开/关,蓝牙开/关)。其他像 Settings.Secure.ANDROID_ID
这样的标识符必须由制造商实施,而且不能保证唯一性。
以下是将数据写入installation文件的示例,该文件将与应用程序本地保存的任何其他数据一起存储。
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();
}
}
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);
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
围绕ANDROID_ID
问题,有许多不同的方法可以解决(有时可能为null
或某个特定型号设备始终返回相同的ID),这些方法各有利弊:
我个人喜欢在Android上使用现有的OpenUDID实现(请参见https://github.com/ylechelle/OpenUDID),也可参考于https://github.com/vieux/OpenUDID。它易于集成,并利用ANDROID_ID
来解决上述问题的后备方案。
我的两分 - 注意这是针对设备 (错) 唯一 ID 而非在 Android 开发者博客 中讨论的安装 ID。
需要注意的是,@emmby 提供的 解决方案 会退回到应用程序 ID,因为 SharedPreferences 在进程间不同步(请参见此处和此处)。所以我完全避免了这个问题。
相反,我将获取 (设备) ID 的各种策略封装在一个枚举中——改变枚举常量的顺序会影响获取 ID 的各种方式的优先级。返回第一个非空 ID 或抛出异常(根据好的 Java 实践,不给 null 赋予含义)。因此,例如我首先选择 TELEPHONY,但 ANDROID_ID 是一个很好的默认选择。 beta:
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);
}
}
}
这里有30多个答案,有些相同,有些独特。本答案基于其中一些答案,其中之一是@Lenn Dolling的答案。
它结合了3个ID并创建了一个32位十六进制字符串。它对我非常有效。
3个ID分别为:
Pseudo-ID - 它基于物理设备规格生成
ANDROID_ID - Settings.Secure.ANDROID_ID
蓝牙地址 - 蓝牙适配器地址
它将返回类似于这样的内容:551F27C060712A72730B0A0F734064B1
注意:您可以始终向longId
字符串添加更多的ID,例如串号、wifi适配器地址、IMEI。这样,您就可以使每个设备更加独特。
@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 "";
}
longId
中并将其存储在文件中,将使它成为最独特的标识符:String uuid = UUID.randomUUID().toString();
。 - Mousa AlfhailyString longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
这可以确保生成的ID是唯一的。 - ᴛʜᴇᴘᴀᴛᴇʟ
ANDROID_ID
,请务必阅读这个答案和这个漏洞。 - Dheeraj Vepakomma