iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0

ResourceManager 與資源解析流程

intro

在 Android 開發中,我們最常接觸的並非直接操作底層系統,而是透過資源 (Resources) 與應用進行互動。無論是字串、圖片、佈局、樣式、顏色,甚至是配置 (Configuration) 對應的資源選擇,都仰賴 Android 的 ResourceManager 進行載入與管理。
對開發者來說,資源管理似乎只是「在 res/ 放檔案,然後用 R.xxx 存取」這麼簡單。但對 Android Framework 而言,資源管理其實是 編譯期 → 打包期 → 運行期 三個階段的完整流程,涵蓋了 AAPT 編譯、資源 ID 生成、APK 中資源表的建構,以及運行時依據螢幕大小、語言、夜間模式等條件進行資源解析 (Resource Resolution)。這個系統保證了 Android 能適配全球各種裝置、地區與語言。

資源管理的重要性

為什麼 Android 要有專屬的資源管理系統?理由如下:

  • 跨語言與地區支持 (Internationalization)
    • 使用 res/values-zh/strings.xml 與 res/values-en/strings.xml,即可針對中文與英文提供不同字串。
    • ResourceManager 會依照使用者系統語言自動選擇。
  • 跨螢幕與裝置配置支持 (Configuration Qualifiers)
    • 針對 hdpi/, xhdpi/, land/, sw600dp/ 目錄提供差異化資源。
    • 適應平板、手機、可折疊裝置。
  • 權限與安全性
    • 資源表會進行 ID 壓縮與優化,避免直接暴露檔案結構。
    • ResourceManager 提供 API 層的統一存取,而非直接透過檔案系統。
  • 效能與快取
    • 運行時 ResourceManager 會快取常用資源,減少 I/O 開銷。
    • 與 AssetManager 整合,支持 Memory Mapping 加快存取速度。

資源生命週期:從編譯到運行

Android 的資源處理可分為三個階段:

  1. 編譯期 (Compile Time)
  • 使用 AAPT2 (Android Asset Packaging Tool 2) 將 res/ 資料夾中的 XML、圖片等資源進行編譯。
  • XML 轉換為二進位格式 (Binary XML)。
  • 生成 R.java 或 R.class,將資源對應到整數 ID。
  • 資源 ID 存放在 resources.arsc (資源索引表)。
  1. 打包期 (Packaging)
  • APK 內含:
    • /res/ → 原始或壓縮資源檔案。
    • /resources.arsc → 資源 ID 與對應的資源項目。
    • /assets/ → 直接提供應用讀取的檔案,不會進入資源表。
  1. 運行期 (Runtime)
  • ResourceManager 與 AssetManager 負責載入資源。
  • 根據 Configuration (如 locale=zh-TW, density=xxhdpi, orientation=landscape) 選擇最合適的資源。
  • 若無法匹配,會 fallback 至 default 目錄。
res/values/strings.xml   → (AAPT2) → R.java  → 編譯進 APK
res/drawable/icon.png    → (壓縮/打包) → resources.arsc
APK 安裝 → ResourceManager 初始化 → 運行期資源解析

ResourceManager 與 AssetManager 的關係

  1. ResourceManager
    Framework 中的核心類別,應用透過 Context.getResources() 取得。
    負責:
  • 提供 API:如 getString(), getDrawable(), getColor()
  • 管理資源快取
  • 處理 Configuration 更新 (如系統語言切換)
  1. AssetManager
  • 底層實作類別,負責與 APK 的 resources.arsc、資源檔案交互。
  • 提供原始檔案存取,透過 memory-mapped I/O 加快讀取。
應用程式
   |
   v
Context → ResourceManager → AssetManager → APK 資源表 / 檔案

資源解析流程 (Resource Resolution)

資源解析是 ResourceManager 的核心,流程如下:

  1. 應用請求資源
  • 例:context.getResources().getString(R.string.app_name)
  1. ResourceManager 查找資源 ID
  • 從 R.string.app_name 得到整數 ID。
  • 交給 AssetManager 查詢對應資源表。
  1. Configuration 匹配
  • 比對系統當前 Configuration (如 locale=zh, screenDensity=xxhdpi)。
  • 在 resources.arsc 中找出最適合的資源版本。
  1. 回傳結果
  • 若是文字,返回字串。
  • 若是圖片,返回 Drawable 物件。
  • 若找不到,會回退至預設資源 (default resources)。
請求資源 ID
   ↓
檢查 Configuration (語言、密度、螢幕方向)
   ↓
是否有對應資源?
   ├─ 是 → 返回對應資源
   └─ 否 → 回退至 default

Configuration 與資源多樣性

  • ResourceManager 支持根據不同裝置特性提供差異化資源。常見的 Configuration Qualifiers 包含:
    • 螢幕方向:res/layout-land/ vs res/layout-port/
    • 螢幕大小與最小寬度:res/layout-sw600dp/
    • 螢幕密度:res/drawable-mdpi/, drawable-xxhdpi/
  • 語言與地區:res/values-en/, res/values-zh-rTW/
    • 夜間模式:res/values-night/
      這讓 Android 能在不同裝置與地區,提供在地化與最佳化的體驗。

資源快取與效能優化

為了避免重複解析資源造成效能浪費,ResourceManager 實作了快取機制:

  • Drawable 快取
    • 圖片資源只會解析一次,後續直接從快取中取用。
  • 字串快取
    • 常用字串保存在 LRU Cache。
  • Configuration 變更時清理
    • 如語言切換、螢幕旋轉,會觸發 ResourceManager 清除快取並重新載入資源。

questions

  • 為什麼 getResources().getDrawable() 會回傳不同結果?
    • 取決於當前 Configuration,例如夜間模式下可能載入 drawable-night。
  • 為什麼 APK 體積很大?
    • 過多資源檔案未針對密度拆分,導致冗餘。
  • 如何減少資源浪費?
    • 使用 resConfig 過濾只需的語言。
  • 使用 density split 拆分 APK。

use cases

  • 多國語系
    • 使用 res/values-zh/ 與 res/values-en/ 提供不同字串。
    • ResourceManager 自動依照系統語言載入。
  • 平板專用佈局
    • 在 res/layout-sw600dp/ 定義平板 UI。
    • 手機仍使用 res/layout/。
  • 夜間模式
    • 使用 res/values-night/ 提供不同顏色主題。

Summary

ResourceManager 是 Android Framework 的基礎元件,負責將編譯後的資源表與 APK 中的實際檔案連結起來,並根據裝置的 Configuration 自動進行最合適的選擇。其運作流程從 編譯 (AAPT2)、打包 (APK)、到 運行時 (Resource Resolution),完整支持了多語系、多裝置、多密度與動態配置切換。


上一篇
#11
下一篇
#13
系列文
安豬複習16
  1. 12
    #11
  2. 13
    #12
  3. 14
    #13
  4. 15
    #14
  5. 16
    #15
完整目錄
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言