Android6.0后获取App设备唯一标示
问题
在用户没有登录的情况下必须确保设备的唯一性,于是惯性思维想到的肯定是使用DevicesId 来作为设备的唯一标识。
但Android6.0后google对权限加以限制和国内rom的不同等原因导致无法一个稳定的唯一标示
常见唯一标示
-
imei
-
Android_id
-
mac地址
-
Installtion ID : UUID
-
Pseudo-Unique ID
IMEI
获取权限
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
获取方法
TelephonyManager telephonyManager =(TelephonyManager)context.getSystemService(context.TELEPHONY_SERVICE);
String imei = telephonyManager.getDeviceId();
问题:
Android6.0后当无法获取到该权限后,方法直接报错,imei作为唯一标示可靠性太差。
Android_id
获取
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);
问题:
Android_id是不需要权限,但它跟手机rom和手机厂商有关(Android_id是设备首次运行随机生成的64位数字)有点手机是获取不到,恢复出厂设置时也会改变,可靠性也较差
Mac地址
获取方法
/**
* Android 6.0 之前(不包括6.0)获取mac地址
* 必须的权限 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
* @param context * @return
*/
public static String getMacDefault(Context context) {
String mac = "";
if (context == null) {
return mac;
}
WifiManager wifi = (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo info = null;
try {
info = wifi.getConnectionInfo();
} catch (Exception e) {
e.printStackTrace();
}
if (info == null) {
return null;
}
mac = info.getMacAddress();
if (!TextUtils.isEmpty(mac)) {
mac = mac.toUpperCase(Locale.ENGLISH);
}
return mac;
}
/**
* Android 6.0-Android 7.0 获取mac地址
*/
public static String getMacAddress() {
String macSerial = null;
String str = "";
try {
Process pp = Runtime.getRuntime().exec("cat/sys/class/net/wlan0/address");
InputStreamReader ir = new InputStreamReader(pp.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
while (null != str) {
str = input.readLine();
if (str != null) {
macSerial = str.trim();//去空格
break;
}
}
} catch (IOException ex) {
// 赋予默认值
ex.printStackTrace();
}
return macSerial;
}
/**
* 通过网络接口取
* @return
*/
private static String getNewMac() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return null;
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:", b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 获取mac地址(适配所有Android版本)
* @return
*/
public static String getMac( Context context) {
String mac = "";
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mac = getMacDefault(context);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
mac = getMacAddress();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mac = getNewMac();
}
return mac;
}
问题:
手机必须具有上网功能,使用场景有局限性
Installtion ID : UUID
这种方式的原理是在程序安装后第一次运行时生成一个ID,该方式和设备唯一标识不一样,不同的应用程序会产生不同的ID,同一个程序重新安装也会不同。所以这不是设备的唯一ID,但是可以保证每个用户的ID是不同的。可以说是用来标识每一份应用程序的唯一ID(即Installtion ID),可以用来跟踪应用的安装数量等。
获取方法
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();
}
}
问题:
无法唯一的标示,多次安装多次不同
Pseudo-Unique ID
/**
* Return pseudo unique ID
*
* @return ID
*/
public static String getUniquePsuedoDeviceID() {
// 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);
// Log.i("", "getUniquePsuedoDeviceID: "+m_szDevIDShort);
// Only devices with API >= 9 have android.os.Build.SERIAL
String serial = null;
try {
serial = 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!
// http://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();
}
问题:
模拟器和刷机带来的一样的id,但能保证99.5%的成功率
总结
到现在发现没有一个是完全靠谱的方式,那就呵呵。
那就其他人是咋解决的呢?发现无外乎这些方法,那大厂是如何解决的呢?
解决
utdid4all-1.1.5.3_proguard.jar
借助上面阿里的jar
UTDevice.getUtdid(AppApplication.in())
获取一个稳定的id
网友评论