此文章已收入Android偶遇雜症合集(持續更新)
1、遇到的問題
在手機設置里的信息上IMEI有15位的數字,但通過代碼獲得卻只有14位,少了最后一位數字。手機重新開機,代碼獲得了正確的15位數字。最終測試現象,獲取方法不可控,返回值可能14位也可能15位,使用時很容易導致數據異常。
2、獲取IMEI的常規方式
public static String getIMEI(Context context){
String imei = "";
try {
TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
imei = tm.getDeviceId();
}else {
Method method = tm.getClass().getMethod("getImei");
imei = (String) method.invoke(tm);
}
} catch (Exception e) {
e.printStackTrace();
}
return imei;
}
3、解決方式
3.1 IMEI的結構
第一部分 TAC,Type Allocation Code,類型分配碼,由8位數字組成(早期是6位), 是區分手機品牌和型號的編碼,該代碼由GSMA及其授權機構分配。其中TAC碼前兩位又是分配機構標識 (Reporting Body Identifier),是授權IMEI碼分配機構的代碼,如01為美國CTIA,35為英國BABT,86為中國TAF。
第二部分 FAC,Final Assembly Code,最終裝配地代碼,由2位數字構成, 僅在早期TAC碼為6位的手機中存在,所以TAC和FAC碼合計一共8位數字。FAC碼用於生產商內部區分生產地代碼。
第三部分 SNR,Serial Number,序列號,由第9位開始的6位數字組成,區分每部手機的生產序列號。
第四部分 CD,Check Digit,驗證碼,由前14位數字通過 Luhn算法計算得出。
由此看出,最后一位是可以直接由前14位計算出來的,那么我們直接判斷長度后再計算一遍就好了。
3.2 Luhn算法
- 將偶數位數字分別乘以2,分別計算個位數和十位數之和
- 將奇數位數字相加,再加上上一步算得的值
- 如果得出的數個位是0則校驗位為0,否則為10減去個位數
如:35 89 01 80 69 72 41
偶數位乘以2得到
5*2=10 、 9*2=18 、 1*2=02 、 0*2=00 、 9*2=18 、 2*2=04 、 1*2=02
計算(奇數位數字)+(上一步計算的偶數位乘積的十位 + 個位),得到
3+(1+0)+8+(1+8)+0+(0+2)+8+(0+0)+6+(1+8)+7+(0+4)+4+(0+2)=63
最后位校驗位為 10-3 = 7
所以完整是358901806972417
3.3 Java實現
public static String getIMEI14To15(String imei) {
int last = 0, i = 0, temp;
while (i < imei.length()){
// 加上奇數位
result += Integer.parseInt(imei.substring(i, ++i));
// 偶數位乘以2
temp = Integer.parseInt(imei.substring(i, ++i)) * 2;
// 加上偶數位的十位和個位,這里做了優化操作
// 偶數位乘2的乘積范圍為[0, 18],小於10直接加,大於10則個位與十位的和等於乘積減9
result += temp < 10 ? temp : temp - 9;
}
// 取最終和的個位
result %= 10;
// 若為0則為0,否則為10減去個位
result = result == 0? 0 : 10 - result;
return imei + result;
}
public static String getIMEI(Context context){
String imei = "";
try {
TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
imei = tm.getDeviceId();
}else {
Method method = tm.getClass().getMethod("getImei");
imei = (String) method.invoke(tm);
}
} catch (Exception e) {
e.printStackTrace();
}
if (imei != null && imei.length() == 14) {
imei = getIMEI14To15(imei);
}
return imei;
}
