首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android 獲取設備序列號完整方案:反射調用 Build.getString("ro.serialno") 兼容低版本

     編輯:Java 相關知識

背景:android.os.Build.SERIAL 的限制

  • API 9 (Android 2.3) 引入 Build.SERIAL
  • API 26+ 需 READ_PHONE_STATE 權限
  • 部分設備返回 "unknown" 或空值
  • 低版本設備完全無此字段

通用解決方案:反射調用隱藏 API

通過反射調用 Build.getString("ro.serialno"),該屬性在底層屬性系統中廣泛存在,兼容性極佳。

完整工具類代碼

import android.os.Build;
import java.lang.reflect.Method;

public class DeviceIdUtils {
    /**
     * 獲取設備序列號,兼容 API 9 以下設備
     * @return 序列號字符串,失敗返回 "unknown"
     */
    public static String getDeviceSerial() {
        String serial = "unknown";
        try {
            Class buildClass = Class.forName("android.os.Build");
            Class stringClass = Class.forName("java.lang.String");
            // 獲取隱藏方法:public String getString(String key)
            Method getStringMethod = buildClass.getDeclaredMethod("getString", stringClass);
            
            if (!getStringMethod.isAccessible()) {
                getStringMethod.setAccessible(true); // 突破訪問限制
            }
            
            // 調用靜態方法,傳入屬性鍵
            Object result = getStringMethod.invoke(null, "ro.serialno");
            if (result instanceof String) {
                serial = (String) result;
            }
        } catch (ClassNotFoundException | NoSuchMethodException | 
                 IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return serial;
    }
}

關鍵技術點解析

技術點 說明
Class.forName 動態加載類,避免編譯期依賴不存在的 API
getDeclaredMethod 獲取聲明的方法(含 private/hidden),不查找父類
setAccessible(true) 關鍵步驟:繞過 Java 訪問檢查,調用隱藏/私有方法
invoke(null, ...) 靜態方法調用,第一個參數為 null
ro.serialno Android 系統屬性鍵,對應 /default.prop 中的 ro.serialno

多重降級策略(生產環境推薦)

public static String getSerialNumber(Context context) {
    // 1. 標準 API (API 9+)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        String serial = Build.SERIAL;
        if (!"unknown".equals(serial) && !TextUtils.isEmpty(serial)) {
            return serial;
        }
    }
    
    // 2. 反射隱藏 API (全版本)
    String serial = getDeviceSerialByReflection();
    if (!"unknown".equals(serial)) return serial;
    
    // 3. 讀取文件 (部分設備)
    serial = readFromFile("/sys/class/android_usb/android0/iSerial");
    if (serial != null) return serial;
    
    // 4. 最後兜底:隨機生成並持久化
    return generateAndSavePseudoId(context);
}

private static String readFromFile(String path) {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    } catch (IOException e) {
        return null;
    }
}

權限與合規注意事項

  • Android 10+:Build.SERIAL、getSerial() 均需 READ_PRIVILEGED_PHONE_STATE(系統應用專用)
  • 隱私合規:序列號屬於設備標識符,GDPR/個信法規下需用戶授權
  • 替代方案:優先使用 Settings.Secure.ANDROID_ID、廣告 ID (GAID) 或自生成 UUID

測試驗證建議

  1. 不同 API 級別模擬器/真機測試
  2. 多廠商設備測試(三星、小米、華為、OPPO 等定制系統行為不同)
  3. 無 SIM 卡、飛行模式、多用戶模式下測試
  4. 記錄失敗案例持續完善降級邏輯

掌握反射調用隱藏系統屬性,是 Android 開發處理碎片化、獲取設備唯一標識的核心技能之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved