1. 程式人生 > >獲取Android裝置的唯一識別符號

獲取Android裝置的唯一識別符號

最近做的一個需求,客戶要求賬號最多繫結三臺裝置。我之所以說是唯一識別符號而不是獲取Android裝置的IMEI是因為IMEI並不是唯一的解決方案,也不一定是最優解,具體還要看需求。

IMEI

先說一下最常用的IMEI,android系統中通常用下面這段程式碼獲取。

/**
 * 獲取手機IMEI號
 * 
 * 需要動態許可權: android.permission.READ_PHONE_STATE
 */
public static String getIMEI(Context context) {
        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(context.TELEPHONY_SERVICE);
        String imei = telephonyManager.getDeviceId();

        return
imei; }

它有3個缺點:

  1. 需要android.permission.READ_PHONE_STATE許可權,它在6.0+系統中是需要動態申請的。如果需求要求App啟動時上報裝置識別符號的話,那麼第一會影響初始化速度,第二還有可能被使用者拒絕授權。

  2. android系統碎片化嚴重,有的手機可能拿不到DeviceId,會返回null或者000000。

  3. 這個方法是隻對有電話功能的裝置有效的,在pad上不起作用。 可以看下方法註釋

/**
     * Returns the unique device ID, for example, the IMEI for GSM and the MEID
     * or ESN for CDMA phones. Return null if device ID is not available.
     *
     * <p>Requires Permission:
     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     */
public String getDeviceId() { try { ITelephony telephony = getITelephony(); if (telephony == null) return null; return telephony.getDeviceId(mContext.getOpPackageName()); } catch (RemoteException ex) { return null; } catch
(NullPointerException ex) { return null; } }

AndroidId

在裝置首次啟動時,系統會隨機生成一個64位的數字,並把這個數字以16進位制字串的形式儲存下來。不需要許可權,平板裝置通用。獲取成功率也較高,缺點是裝置恢復出廠設定會重置。另外就是某些廠商的低版本系統會有bug,返回的都是相同的AndroidId。獲取方式如下:

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

Serial Number

Android系統2.3版本以上可以通過下面的方法得到Serial Number,且非手機裝置也可以通過該介面獲取。不需要許可權,通用性也較高,但我測試發現紅米手機返回的是 0123456789ABCDEF 明顯是一個順序的非隨機字串。也不一定靠譜。

String SerialNumber = android.os.Build.SERIAL; 

其他方法

經常被提到的還有下面幾個

  1. Mac地址 – 屬於Android系統的保護資訊,高版本系統獲取的話容易失敗,返回0000000;

  2. SimSerialNum – 顯而易見,只能用在插了Sim的裝置上,通用性不好。而且需要android.permission.READ_PHONE_STATE許可權

總結

綜上述,AndroidId 和 Serial Number 的通用性都較好,並且不受許可權限制,如果刷機和恢復出廠設定會導致裝置識別符號重置這一點可以接受的話,那麼將他們組合使用時,唯一性就可以應付絕大多數裝置了。

String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
String id = androidID + Build.SERIAL;

但還可以優化一下。直接暴露使用者的裝置資訊並不是一個好的選擇,既然我需要的只是一個唯一標識,那麼將他們轉化成Md5即可,格式也更整齊。

/**
 * Created by Yomii on 2017/6/8.
 */

public class DeviceUtils {

    public static String getUniqueId(Context context){
        String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
        String id = androidID + Build.SERIAL;
        try {
            return toMD5(id);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return id;
        }
    }


    private static String toMD5(String text) throws NoSuchAlgorithmException {
        //獲取摘要器 MessageDigest
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        //通過摘要器對字串的二進位制位元組陣列進行hash計算
        byte[] digest = messageDigest.digest(text.getBytes());

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < digest.length; i++) {
            //迴圈每個字元 將計算結果轉化為正整數;
            int digestInt = digest[i] & 0xff;
            //將10進位制轉化為較短的16進位制
            String hexString = Integer.toHexString(digestInt);
            //轉化結果如果是個位數會省略0,因此判斷並補0
            if (hexString.length() < 2) {
                sb.append(0);
            }
            //將迴圈結果新增到緩衝區
            sb.append(hexString);
        }
        //返回整個結果
        return sb.toString();
    }
}