通過Android應用程序資源的編譯和打包過程,最終得到的應用程序資源就與應用程序代碼一起打包在一個APK文件中。Android應用程序在運行的過程中,是通過一個稱為AssetManager的資源管理器來讀取打包在APK文件裡面的資源文件的。
訪問app內部的資源:應用程序的每一個Activity組件都關聯有一個ContextImpl對象,這個ContextImpl對象就是用來描述Activity組件的運行上下文環境的。Activity組件是從Context類繼承下來的,而ContextImpl同樣是從Context類繼承下來的。在Activity組件調用的大部分成員函數都是轉發給與它所關聯的一個ContextImpl對象的對應的成員函數來處理的,其中就包括用來訪問應用程序資源的兩個成員函數getResources和getAssets。ContextImpl類的成員函數getResources返回的是一個Resources對象,有了這個Resources對象,就可以通過資源ID來訪問那些被編譯過的應用程序資源。ContextImpl類的成員函數getAssets返回的是一個AssetManager對象,有了這個AssetManager對象,就可以通過文件名來訪問那些被編譯過或者沒有被編譯過的應用程序資源文件。
Resources類的實現:事實上,Resources類也是通過AssetManager類來訪問那些被編譯過的應用程序資源文件的,不過在訪問之前,它會先根據資源ID查找得到對應的資源文件名。在Android系統中,一個進程是可以同時加載多個應用程序的,也就是可以同時加載多個APK文件。每一個APK文件在進程中都對應有一個全局的Resourses對象以及一個全局的AssetManager對象。其中,這個全局的Resourses對象保存在一個對應的ContextImpl對象的成員變量mResources中,而這個全局的AssetManager對象保存在這個全局的Resourses對象的成員變量mAssets中。Resources類有一個成員函數getAssets,通過它就可以獲得保存在Resources類的成員變量mAssets中的AssetManager,例如,ContextImpl類的成員函數getAssets就是通過調用其成員變量mResources所指向的一個Resources對象的成員函數getAssets來獲得一個可以用來訪問應用程序的非編譯資源文件的AssetManager。
訪問系統資源:Android應用程序除了要訪問自己的資源之外,還需要訪問系統的資源。系統的資源打包在/system/framework/framework-res.apk文件中,它在應用程序進程中是通過一個單獨的Resources對象和一個單獨的AssetManager對象來管理的。這個單獨的Resources對象就保存在Resources類的靜態成員變量mSystem中,我們可以通過Resources類的靜態成員函數getSystem就可以獲得這個Resources對象,而這個單獨的AssetManager對象就保存在AssetManager類的靜態成員變量sSystem中,可以通過AssetManager類的靜態成員函數getSystem同樣可以獲得這個AssetManager對象。