安卓项目实战之:更优雅的方式获取安卓设备的唯一标识码

前言

最近,项目中有一个需求,需要发送当前设备的机器码给后台,最开始采用了DEVICE_ID,获取方式如下:

TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); 
String DEVICE_ID = tm.getDeviceId();

它会根据不同的手机设备返回IMEI,MEID或者ESN码.

这是Android系统为开发者提供的用于标识手机设备的串号,也是各种方法中普适性较高的,可以说几乎所有的设备都可以返回这个串号,并且唯一性良好。

但是存在以下问题:
1,对于没有通话硬件功能的非手机设备如平板电脑,系统中也就没有TELEPHONY_SERVICE,自然也就无法通过上面的方法获得DEVICE_ID。
2,获取该参数需要权限android.permission.READ_PHONE_STATE,并且该权限在6.0级以上系统上需要动态申请,如果只是为了获取DEVICE_ID而没有用到其他的通话功能,申请这个权限一来大才小用,二来部分用户会怀疑软件的安全性。
3,获取该机器码的操作是在application中作为全局配置来完成的,因为权限关系,导致第一次打开出现闪退。

更优雅的方式:AndroidId+Serial Number加盐加密

AndroidId

在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来。不需要权限,平板设备通用。获取成功率也较高,缺点是设备恢复出厂设置会重置。另外就是某些厂商的低版本系统会有bug,返回的都是相同的AndroidId。获取方式如下:

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

Serial Number

Android系统2.3版本以上可以通过下面的方法得到Serial Number,且非手机设备也可以通过该接口获取。不需要权限,通用性也较高:

String SerialNumber = android.os.Build.SERIAL; 

总结

AndroidId 和 Serial Number 的通用性都较好,并且不受权限限制,如果刷机和恢复出厂设置会导致设备标识符重置这一点可以接受的话,那么将他们组合使用时,唯一性就可以应付绝大多数设备了,但还可以优化一下。直接暴露用户的设备信息并不是一个好的选择,既然我需要的只是一个唯一标识,那么将他们转化成Md5即可,格式也更整齐:

String androidID = Settings.Secure.getString(mActivity.getContentResolver(), Settings.Secure.ANDROID_ID);
String deviceId = androidID + android.os.Build.SERIAL;

最终加密生成的32位机器码为:MD5Utils.encryptInfo(deviceId);

MD5加密工具类

public class MD5Utils {

    //公盐(前缀+后缀)
    private static final String SALT_START = "a0bjd35ff4kk9t6";
    private static final String SALT_END = "h3m8sh3l3s5lls";
    //十六进制下数字到字符的映射数组
    private final static String[] hexDigits = {"0", "1", "2", "3", "4",
            "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     *
     * @param source 需要加密的字符串
     * @return MD5加密字符串
     */
    public static String encryptInfo(String source) {
        // 需要加密字符串+公盐
        return encodeByMD5(SALT_START + source + SALT_END);
    }

    /**
     * md5加密算法
     *
     * @param originString
     * @return
     */
    private static String encodeByMD5(String originString) {
        if (originString != null) {
            try {
                // 创建具有指定算法名称的信息摘要
                MessageDigest md = MessageDigest.getInstance("MD5");
                // 获取二进制
                byte[] bytes = originString.getBytes();
                // 执行加密并获得加密的结果,结果为byte字节数组
                byte[] results = md.digest(bytes);
                // 将得到的字节数组变成字符串返回
                String resultString = byteArrayToHexString(results);
                return resultString.toUpperCase();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 转换字节数组为十六进制字符串
     *
     * @param字节数组
     * @return 十六进制字符串
     */
    private static String byteArrayToHexString(byte[] b) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            resultSb.append(byteToHexString(b[i]));
        }
        return resultSb.toString();
    }

    /**
     * 将一个字节转化成十六进制形式的字符串
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n = 256 + n;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

}

猜你喜欢

转载自blog.csdn.net/gpf1320253667/article/details/86151123
今日推荐