Android关于设备唯一标识符的获取,适配Android10

Posted 陳英傑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android关于设备唯一标识符的获取,适配Android10相关的知识,希望对你有一定的参考价值。

1、IMEI

获取IMEI是通过TelephonyManager对象,TelephonyManager对象的获取方式是通过context.getSystemService(),传入不同的值获取到的Manager对象不同。

TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);

主要分以下几种情况:

  • android 6.0以下,无需权限
    获取方式:tm.getDeviceId();
  • Android 6.0 - Android 8.0,需要READ_PHONE_STATE权限,如果用户拒绝权限会抛出SecurityException异常。
    获取方式:tm.getDeviceId();
  • Android 8.0 - Android 10,需要READ_PHONE_STATE权限,如果用户拒绝权限会抛出SecurityException异常。
    获取方式:tm.getImei();
    但是此方法获取的imei只有一个,如果手机是双卡的是有两个imei值的,可以使用动态调用的方式都获取到:
public static String getIMEI(Context context) 
	TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    try 
         Method method = manager.getClass().getMethod("getImei", int.class);
         String imei1 = (String) method.invoke(manager, 0);
         String imei2 = (String) method.invoke(manager, 1);
         return imei1;// 根据需求返回
      catch (Exception e) 
         e.printStackTrace();
     
	return "";

  • Android 10 以上,即使你申请了READ_PHONE_STATE权限,依然会抛出异常,所以IMEI不能用了。
2、序列号

序列号也和Android版本有关,不同版本获取方式不同,主要分以下情况:

  • Android 8.0以下,不需要权限,可以通过android.os.Build.SERIAL获取。
  • Android 8.0 - Android 10,通过以上方式获取到的是unknown,此方式已废弃;需要READ_PHONE_STATE权限,通过android.os.Build.getSerial()获取,如果用户拒绝了,抛出SecurityException异常。
  • Android 10以上,即使有READ_PHONE_STATE权限也会抛出SecurityException异常,和imei一样,Android10以上不能获取了。
3、Mac地址

mac地址分以下几种情况:

  • Android 6.0以下,需要ACCESS_WIFI_STATE权限,通过以下方式获取:
private String getMacAddress(Context context) 
    WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    return manager.getConnectionInfo().getMacAddress();

  • Android 6.0以后,上面方法获取的结果都是02:00:00:00:00:00,用读取系统文件方式获取:
	/**
     * Android 6.0 - Android 7.0(不包含)
     * @return
     */
    private static String getMacAddress() 
        String macSerial = null;
        String str = "";
        InputStreamReader ir = null;
        LineNumberReader input = null;
        try 
            Process pp = Runtime.getRuntime().exec("cat /sys/class/net/wlan0/address");
            ir = new InputStreamReader(pp.getInputStream(), StandardCharsets.UTF_8);
            input = new LineNumberReader(ir);
            for (; null != str; ) 
                str = input.readLine();
                if (str != null) 
                    macSerial = str.trim();
                    break;
                
            
         catch (IOException ex) 
            ex.printStackTrace();
         finally 
            close(input);
            close(ir);
        
        return macSerial;
    

    private static void close(Closeable closeable) 
        if (closeable != null) 
            try 
                closeable.close();
             catch (Throwable e) 
                e.printStackTrace();
            
        
    

但是此方法在Android 7.0以后也不能用了,会抛出FileNotFoundException异常,取而代之的是通过扫描所有网络接口来获取:

	private static String getAddressMacByInterface() 
        try 
            List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface nif : all) 
                if (nif.getName().equalsIgnoreCase("wlan0")) 
                    byte[] macBytes = nif.getHardwareAddress();
                    if (macBytes == null) 
                        return "";
                    
                    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 e) 
            e.printStackTrace();
        
        return "";
    

此方式也是目前获取mac最稳定的方式,但是Android 10加入了mac地址随机化特性,目前市面上大部分手机不支持此特性,需要在开发者选项中设置,普通用户一般连开发者模式都是关闭的,所以暂时可以放心使用,但是随着Android版本的迭代,不排除Google官方把此特性默认设置为打开的。

4、ANDROID_ID

android_id是设备的系统首次启动生成的一串字符,基本可以保证唯一性,获取方式:

String androidId = Settings.System.getString(getContentResolver(), Settings.Secure.ANDROID_ID);

与上面的几种相比,android_id获取比较简单,没有权限限制,也不会抛出异常,但是root、刷机或恢复出厂设置都会导致设备的ANDROID_ID重置。

总结:

总的来说,为设备添加唯一标识可以参照以下优先级:
1、ANDROID_ID:系统首次启动生成,获取方式安全,但是不稳定。
2、MAC地址:获取方式不安全,比较稳定。
由于Android 10的发布,序列号和IMEI基本可以放弃使用了;
3、Android还提供了一个生成id的工具类UUID,可以生成一串随机字符,保存到本地来使用。
4、使用移动安全联盟提供的方法(未尝试过)。

反正由于Android的碎片化,需要开发者不断的适配、更新,如果长时间不适配,说不定哪天系统更新了你的应用就崩了。
关于以上方法的工具类下载

以上是关于Android关于设备唯一标识符的获取,适配Android10的主要内容,如果未能解决你的问题,请参考以下文章

如何获取Android唯一标识

如何获取Android唯一标识

Android能够获取到唯一的设备ID吗

MarshMallow 警告“不建议获取设备标识符”后的唯一 Android 设备 ID?

如何获取Android唯一标识

Android开发 - 获取Android设备的唯一标识码(Android 6.0或更高)