原文出處——>[Android資源管理框架(Asset Manager)簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/8738877)
Android應用程序主要由兩部分內容組成:代碼和資源。資源主要就是指那些與UI相關的東西,例如UI布局、字符串和圖片等。代碼和資源分開可以使得應用程序在運行時根據實際需要來組織UI。這樣就可使得應用程序只需要編譯一次,就可以支持不同的UI布局。這種特性使得應用程序在運行時可以適應不同的屏幕大小和密度,以及不同的國家和語言等。在本文中,我們就簡要介紹Android的資源管理框架,以及制定學習計劃。
在軟件開發中,說到代碼與資源分離,最容易想到的可能就是Web開發了。在Web開發中,我們一般會通過CSS文件來描述HTML頁面的展現形式,也就是通過CSS來控制HTML頁面的UI。這樣就可以很方便地進行Web開發和維護。例如,當我們要更改HTML頁面的UI時,只要修改相應的CSS文件就可以了。注意,這些CSS文件都是在運行時加載的。這樣我們就可以根據HTML頁面的運行環境來加載不同的CSS文件,例如,根據不同的地區或者語言來選擇不同的CSS文件,從而實現國際化。
再來看PC客戶端軟件的開發。開始的時候,微軟的MFC應用程序框架非常流行。在開發MFC程序的時候,代碼和資源同樣也是分開的,例如,程序的界面一般都是通過一個RC文件來描述的。不過我們一般都是在Visual Studio里面通過可視化界面來編輯RC文件的,即一般都不會直接手動去操作RC文件,所以我們一般都不怎么意識到其實RC文件和CSS文件一樣,都是用來描述程序的界面的。實際上,RC文件和CSS文件一樣,都是可以看作是一個界面配置文件,而且它們的配置信息都是通過文字來描述的,只不過這些文字描述要遵循一定的規范。
隨著PC客戶端軟件的發展,微軟的MFC應用程序框架顯得有些力不從心了,其中的一個原因就是它的界面比較丑陋。如果要對MFC應用程序的UI進行美化以及個性化的話,是要費比較大的勁的,這嚴重地影響了軟件開發效率,特別是不適合要進行快速迭代開發的互聯網客戶端軟件。微軟后來又開發了另外一套應用程序開發框架WPF。WPF同樣是使用一種稱為XAML的文件來描述應用程序的界面的。實際上,包括現在Win 8的Metro界面,也同樣是通過XAML文件來描述應用程序界面的。XAML文件是一種XML文件,它具有更好的可讀性,非常方便編輯以及維護。
在PC客戶端軟件的發展過程中,還有一種不得不提的應用程序框架——QT。QT最初由Trolltech公司開發,后來被Nokia收購。隨著Meego的沒落,如日沖天的Nokia也沒落了,Qt又被賣給了芬蘭的另外一家IT服務公司Digia。QT也算得是一套優秀的應用程序框架,而且它是跨平臺的。QT同樣也是通過一種稱為QML的文件來描述應用程序的界面的,不過QML文件不是XML格式的,它的格式有點類似Web頁面的CSS。
類似這種采用XML文件來描述界面的PC客戶端軟件開發框架其實還有很多,例如,迅雷用的Bolt界面引擎,以及騰訊QQ用的GF界面引擎,它們都同樣是通過XML文件來描述程序界面的,并且做成代碼和界面描述文件分離。
最后看iOS應用程序的開發,它的界面和代碼同樣是分開,并且通過一種稱為XIB的文件來描述界面。XIB文件實際上也是一個XML文件,因此,它也是非常方便編輯以及維護的。
從上面的分析就可以看出,無論是Web應用程序,還是PC客戶端應用程序,以及移動客戶端應用程序,它們都無一例外地將代碼與界面分離,并且界面都是通過描述性的文字來描述的,這種描述性的文字越來越傾向于使用XML格式。
Android應用程序作為一種移動客戶端應用程序,它同樣也是毫無意外地將代碼邏輯和界面資源進行分離,但是它的資源管理方式與傳統的Web應用程序和PC客戶端應用程序以及iOS應用程序相比會更復雜一些,這是因為Android應用程序可能會運行在各種大小和密度不等的設備之上。接下來我們就將注意力集中在Android應用程序資源的組織和管理之上。
我們首先看Android應用程序資源的分類。Android應用程序資源可以分為兩大類,分別是assets和res:
1. assets。assets類資源放在工程根目錄的assets子目錄下,它里面保存的是一些原始的文件,可以以任何方式來進行組織。這些文件最終會被原裝不動地打包在apk文件中。如果我們要在程序中訪問這些文件,那么就需要指定文件名來訪問。例如,假設在assets目錄下有一個名稱為filename的文件,那么就可以使用以下代碼來訪問它:
~~~
AssetManager am= getAssets();
InputStream is = assset.open("filename");
~~~
2. res。res類資源放在工程根目錄的res子目錄下,它里面保存的文件大多數都會被編譯,并且都會被賦予資源ID。這樣我們就可以在程序中通過ID來訪問res類的資源。res類資源按照不同的用途可以進一步劃分為以下9種子類型:
- animator。這類資源以XML文件保存在res/animator目錄下,用來描述屬性動畫。屬性動畫通過改變對象的屬性來實現動畫效果,例如,通過不斷地修改對象的坐標值來實現對象移動動畫,又如,通過不斷地修改對象的Alpha通道值來實現對象的漸變效果。
- anim。這類資源以XML文件保存在res/anim目錄下,用來描述補間動畫。補間動畫和屬性動畫不同,它不是通過修改對象的屬性來實現,而是在對象的原來形狀或者位置的基礎上實現一個變換來得到的,例如,對對象施加一個旋轉變換,就可以獲得一個旋轉動畫,又如,對對象實施一個縮放變換,就可以獲得一個縮放動畫。從數學上來講,就是在對象的原來形狀或者位置的基礎上施加一個變換矩陣來實現動畫效果。注意,在動畫的執行過程中,對象的屬性是始終保持不變的,我們看到的只不過是它的一個變形副本。
- color。這類資源以XML文件保存在res/color目錄下,用描述對象顏色狀態選擇子。例如,我們可以定義一個選擇子,規定一個對象在不同狀態下顯示不同的顏色。對象的狀態可以劃分為pressed、focused、selected、checkable、checked、enabled和window_focused等7種。
- drawable。這類資源以XML或者Bitmap文件保存在res/drawable目錄下,用來描述可繪制對象。例如,我們可以在里面放置一些圖片(.png, .9.png, .jpg, .gif),來作為程序界面視圖的背景圖。注意,保存在這個目錄中的Bitmap文件在打包的過程中,可能會被優化的。例如,一個不需要多于256色的真彩色PNG文件可能會被轉換成一個只有8位調色板的PNG面板,這樣就可以無損地壓縮圖片,以減少圖片所占用的內存資源。
- layout。這類資源以XML文件保存在res/layout目錄下,用來描述應用程序界面布局。
- menu。這類資源以XML文件保存在res/menu目錄下,用來描述應用程序菜單,例如,Options Menu、Context Menu和Sub Menu。
- raw。這類資源以任意格式的文件保存在res/raw目錄下,它們和assets類資源一樣,都是原裝不動地打包在apk文件中的,不過它們會被賦予資源ID,這樣我們就可以在程序中通過ID來訪問它們。例如,假設在res/raw目錄下有一個名稱為filename的文件,并且它在編譯的過程,被賦予的資源ID為R.raw.filename,那么就可以使用以下代碼來訪問它:
~~~
Resources res = getResources();
InputStream is = res .openRawResource(R.raw.filename);
~~~
- values。這類資源以XML文件保存在res/values目錄下,用來描述一些簡單值,例如,數組、顏色、尺寸、字符串和樣式值等,一般來說,這六種不同的值分別保存在名稱為arrays.xml、colors.xml、dimens.xml、strings.xml和styles.xml文件中
- xml。這類資源以XML文件保存在res/xml目錄下,一般就是用來描述應用程序的配置信息。
注意,上述9種類型的資源文件,除了raw類型資源,以及Bitmap文件的drawable類型資源之外,其它的資源文件均為文本格式的XML文件,它們在打包的過程中,會被編譯成二進制格式的XML文件。這些二進制格式的XML文件分別有一個字符串資源池,用來保存文件中引用到的每一個字符串,包括XML元素標簽、屬性名稱、屬性值,以及其它的一切文本值所使用到的字符串。這樣原來在文本格式的XML文件中的每一個放置字符串的地方在二進制格式的XML文件中都被替換成一個索引到字符串資源池的整數值。這樣做有兩個好處:
* A. 文件占用更小。例如,假設在原來的文本格式的XML文件中,有四個地方使用的都是同一個字符串,那么在最終編譯出來的二進制格式的XML文件中,字符串資源池只有一份字符串值,而引用它的四個地方只占用一個整數值。
* B. 解析速度更快。由于在二進制格式的XML文件中,所有的XML元素標簽和屬性等值都是使用整數來描述的,因此,在解析的過程中,就不再需要進行字符串解析,這樣就可以提高解析速度。
還有另外一個地方需要注意的是,每一個res資源在編譯的打包完成之后,都會被分配一個資源ID,這些資源ID被終會被定義為Java常量值,保存在一個R.java文件中,與應用程序的其它源文件一起被編譯到程序中,這樣我們就可以在程序或者資源文件中通過這些ID常量來訪問指定的資源。
我們接下來再看應用程序資源的組織。應用程序資源的組織方式有18個維度,如圖1所示:

圖1 應用程序資源的組織方式
注意,圖1的表格是來自于官方文檔的,它的詳細描述可以參考:http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources。
這里有一點需要說明的是,表格中的18個維度是按照優先級從最大到小排列的,這個優先級次序可以幫助系統根據機器的本地配置來在應用程序資源目錄中找到最合適的資源來使用。
具體來說,Android資源管理框架按照圖2所示的算法流程來在應用程序資源目錄中選擇最合適的資源:

圖2 應用程序資源的匹配算法
注意,圖2的算法流程圖是來自于官方文檔的,它的詳細描述可以參考:http://developer.android.com/guide/topics/resources/providing-resources.html#BestMatch。
我們同樣是通過上述官方文檔中的例子來說明上述應用程序資源匹配算法的執行過程。
假設一個應用程序的drawable資源按照以下方式來組織:
~~~
drawable/
drawable-en/
drawable-fr-rCA/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
~~~
并且該應用程序所運行在的設置的配置情況如下所示:
~~~
Locale = en-GB
Screen orientation = port
Screen pixel density = hdpi
Touchscreen type = notouch
Primary text input method = 12key
~~~
根據圖2所示的算法,Android資源管理框架按照以下步驟來選擇一個drawable資源:
* Step 1. 消除與設備配置沖突的drawable目錄,即drawable-fr-rCA目錄,因為設備設置的語言是en-GB。
~~~
drawable/
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
~~~
* Step 2. 從MMC開始,選擇一個資源組織維度來過渡從Step 1篩選后剩下來的目錄。
* Step 3. 檢查Step 2選擇的維度是否有對應的資源目錄。如果沒有,就返回到Step 2繼續處理。如果有,那么就繼續往下執行Step 4。在我們這個例子中,要一直重復執行Step 2,直到檢查到language這個維度時。
* Step 4. 消除那些不包含有Step 2所選擇的資源維度的目錄。在我們這個例子中,就是要消除那些不包含有en這個language的目錄:
~~~
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
~~~
* Step 5. 繼續執行Step 2、Step 3和Step 4,直到找到一個最匹配的資源目錄為止,即剩下最后一個目錄為止。在我們這個例子中,下一個要檢查的維度是screen orienation。由于設備的screen orienation為port,因此,所有不包含有port資源維度的目錄將被消除:
~~~
drawable-en-port/
~~~
最后剩下來的目錄就只有drawable-en-port,因此,它就是最匹配的資源目錄了,這時候所有drawable類型的資源都可以從這個目錄中獲取。
注意,我們在編譯和打包應用程序資源的過程中,會生成一個resources.arsc文件,這個文件記錄了所有的應用程序資源目錄的信息,包括每一個資源名稱、類型、值、ID以及所配置的維度信息。我們可以將這個resources.arsc文件想象成是一個資源索引表,這個資源索引表在給定資源ID和設備配置信息的情況下,能夠在應用程序的資源目錄中快速地找到最匹配的資源。
最后,我們可以通過圖3來總結應用程序資源的編譯、打包以及查找過程:

圖3 應用程序資源的編譯、打包以及查找過程
通過圖3我們就可以看出:
* A. 除了assets和res/raw資源被原裝不動地打包進APK之外,其它的資源都會被編譯或者處理。
* B. 除了assets資源之外,其它的資源都會被賦予一個資源ID。
* C. 打包工具負責編譯和打包資源,編譯完成之后,會生成一個resources.arsc文件和一個R.java,前者保存的是一個資源索引表,后者定義了各個資源ID常量。
* D. 應用程序配置文件AndroidManifest.xml同樣會被編譯成二進制的XML文件,然后再打包到APK里面去。
* E. 應用程序在運行時通過AssetManager來訪問資源,或通過資源ID來訪問,或通過文件名來訪問。
在接下來的一系列文章中,我們主要關注以下三個關鍵情景:
1. 應用程序資源的編譯和打包過程;
2. 應用程序資源的初始化過程;
3. 應用程序資源的查找過程。
通過這個三個情景,我們基本上就可以了解Android系統的資源管理框架了,敬請關注。不過在閱讀這個系列的文章之前,希望讀者可以先了解一下Android應用程序資源的基礎知識,因為這個系列的文章不會陷入到這些基礎知識中去,具體可以參考以下官方文檔:
A. http://developer.android.com/guide/topics/resources/index.html。
B. http://developer.android.com/guide/practices/screens_support.html。
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析