資源檔
作為一枚coder,做介面,很多時候都是一場夢魘。很多時候,我們會感覺對於底層邏輯實現的很有把握性,哪怕需求一直在變,也可以通過不斷的重構一直跟進,一切盡在掌握。但遭遇介面,往往就不再如此,它的好壞總是和審美、體驗之類的詞彙扯在一起,在鳳姐芙蓉出沒的年頭,談審美成為一件恐怖的事情。你可能會被要求不停的改代碼,就為了移動一個像素,調整一枚按鈕,瑣碎而無聊。
為了改變這樣的狀況,挽救coder們於水生活熱之中,很多開發平台,都採用了類似於資源檔的解決方案。此類方案的基本思想是,將介面的實現與底層邏輯的實現完全剝離開來,用資源檔這樣的東西來描述介面。資源檔的描述語言,往往是結構化很強,比如Html,Xml(及其變形體)之類的。於開發語言相比,此類語言邏輯性較弱但結構更好可讀性更強更容易理解,並對自動化工具非常友好,可以於介面的拖拽配置結合的更加完美。這樣的剝離,可以是的底層邏輯和上層介面獨立變化,甚至不同的人員開發(這一點在web開發上表現的應該很明顯...),兩者之間的耦合性非常的小,coder們的負擔,陡然減少(好吧,一個很挫的資源架構也會額外增加開發人員的負擔,Symbian同學,請不要對號入座...)。
結構和格式Android的資源檔,是由
目錄結構,
Xml格式的檔案,和
純資料檔案構成。從格式上來看,無疑,學習門檻非常低。Xml作為coder們的瑞士軍刀,哪怕使不習慣,弄得清楚並會用至少是沒有問題。從配套的工具來看,Android的ADT,提供了一套可視化的組態工具,說不上特別好用,但至少是差強人意能湊合著用,比不上iPhone的,調戲Symbian還是沒有問題的[強檔廣告首播:有道詞典 for iPhone新版火熱上線,增加了超強單詞本功能,特有的觸電式顫抖單詞切換功能,讓你欲罷不能,持有相關裝置的童鞋不要猶豫,一擁而上吧...]。
Android的資源檔,
覆蓋面超級廣,只要是和介面相關的,都可以用資源檔表示,比如:UI的樣式,菜單,設定檔,各種描述性字串,圖片,音頻視頻檔案,動畫,顏色,尺寸,風格和樣式,等等等。所有的資源檔(不考慮asset,它和討論暫無關聯...),都放在
res目錄下,不同類別的資源,需要放置在不同的
特定名稱的子檔案夾中,或者是寫在特定檔案名稱的檔案中(或者ms不是必須的,但,不用在這裡特立獨行,尋章辦事也挺好...)。比如,所有作為UI背景之類的圖片,都需要扔在
drawable這類的檔案夾中,所有字串相關的,都會放到
values目錄下形如
strings.xml這樣的檔案中(如所示,是一個資源檔目錄結構的...)。
每個xml檔案,都有一定的約定。比如一個字串,會放在<string></string>這樣的xml element中(如所示...),你可以通過eclipse的ADT外掛程式提供的可是介面去填而不關注具體規範,也可以直接人肉打造,前者對於新手來說更為直觀,後者對於老鳥而言更為迅捷。可配置性程式邏輯總是不變應萬變的,但介面往往是需要能夠72變。首先一種變化因素,就是狀態。想象一下,我們往往會有這樣類似的需求,一個按鈕,我們需要沒有按下去的時候是一種背景,按的過程中刷的變成另一副模樣,當它可用的時候需要鮮鮮亮的一個樣子,停用時候最好是灰不溜秋沒人願點的慫樣,諸如此類。傳統編程模型下(Symbian,哥叫你出來當模特...),我們總是需要不厭其煩的用代碼控制這樣的事情。監聽不同的事件,見縫插針的切換背景,並祈禱上天,千萬別讓哥調整,否則哥和你沒完。
在Android中,做這個事情,變得簡單許多,通過預設的一些Xml屬性,能夠輕鬆的搞定。如所示,是Radio Button的背景。通過搭配不同的屬性,就可以自動轉換背景。比如第一個<item>,說的是當Radio Button被選中,並且具有焦點的時候,顯示btn_radio_on這幅圖片,而最後一個<item>,說的是前述條件都不滿足,並且處於選中狀態,那麼顯示btn_radio_on這幅圖片。另外一個更易變的因素,就是手機硬體/軟體環境了,畢竟,不是家家都是蘋果,一個平台搭一款手機,手機款形多樣化,幾乎是避免不了的問題。沒有人希望自己做的軟體在大螢幕手機上閃亮光鮮,換個小螢幕就慘不忍睹,豎屏看像那麼回事橫屏看就擠做一團。還有就是語言環境了,做為一個有國際眼光的coder,作面向世界的NB軟體是咱的夢想,但我們不能因為自己的夢想逼迫大家都去學中文,做一款軟體可以根據手機的語言環境選擇最合適展示的語言,很多時候,是一個需要具備的功能點。
在Android中,實現這些,都是舉手之勞。方法就是將和環境相關的資源,放入特定名稱的檔案夾中。比如,表示簡體中文字元資訊的資源,可以放到values-zh-rCN中去,當系統語言環境為簡體中文時,就會呈現出中文的字元資訊。在Android中,很多相關配置項,都可以按照這樣的方式參與到資源自適應的活動中來,包括螢幕大小,螢幕朝向,螢幕解析度,語言環境,觸屏類型,SDK版本等等。系統會給所有配置項一個優先順序(或者說權重,次序之類的),當使用者提供了多份資源的時候,系統會根據優先順序從高到底淘汰備選資源,如果淘汰僅剩了一個,那就是最符合當前系統軟硬體語言環境的資源項,如果一個不剩,擇啟用預設項(最是形如values這樣沒有任何尾巴目錄中的資源...)。因此,預設的資源是非常重要的,它必須是其他所有可選資源項的超集,否則在資源選擇失敗的情況下,應用會淒涼的崩潰。關於資源配置,以及選擇的詳情,參見SDK中的:guide/topics/resources/resources-i18n.html部分。
R類在使用資源後,介面邏輯與底層邏輯的耦合被降低了,但這不意味著,兩者沒有關聯了。比如,需要為某個按鈕增加一個點擊事件,就需要定位到所需的那個按鈕;再比如,你需要使用某個字串資源,通知使用者某件事情,就需要能定位到資源中放置的該字串。最顯而易見的一種方式,就是通過字串比較,用
名字資訊在資源的xml描述檔案中定位到所需的內容,載入並使用。這種方式,解決了尋找的問題,但反覆的字串比較,勢必帶來嚴重的效率隱患。因此,在Android中,類似於Symbian的方法,引入了一個R類。
它的基本思想是,通過增加一個額外的編譯器,為所有的資源項,都賦予一個32位的整形數來表示,同一個資源像的不同配置,都使用同一個id。這個整形數,就相當於這個資源項的門牌號碼,能夠協助定位到對應的資源項。所有的這些整形數,都以常量的方式,整合到一個Java類中,這個類就是R類。這樣,在程式中,就可以通過使用這個R類,來尋找所需的資源,這就將字串比較,簡化成了一個整形數的比較,大大的節約了開銷。
不得不說,這整套邏輯和Symbian中的資源檔先行編譯一致。但兩者很不同的點在於Symbian中的整形數,代表的是一個二進位流的位移量,資源中的內容在編譯時間決定了。而Android中的整形數,是一個有邏輯意義的數值,它表達了這個資源所處的資源套件,類別,和腳標,它的具體內容在運行時才確定,這使得它的靈活性大大增強,付出的則是一定的效率代價。
實現按照慣例,還是要說實現的,以一個尋找流程為樣本。當在Activity中需要使用字串的,會調用它的getString方法,傳入R.stirng.xxx的一個整形數,換取一個符合當前機器環境配置的字串。getString,追根溯源,來到AssetManager類中。Asset類,其實是一個空殼,它僅僅是提供了一些便利的介面,而將請求,通過JNI的介面,傳入到了底層C++實現的類庫中。在底層的實現,主要是在C++實現的,AssetManager,ResourceTypes等等之中。其中:
- JNI檔案在:framework/base/core/jni
- 標頭檔在:framework/base/include/utils
- CPP檔案在:framework/base/libs/utils
具體實現,和前述的演算法邏輯是一致的。每一個資源的id,32位,
高8位表示資源套件,
低16位用於描述腳標,
中間8位,用來說明類別。所有資源中的檔案,都被預先處理了,放入到了一系列的隊列和表中,通過id,可以查到具體的位置。然後根據緩衝的環境設定對象,跑一次淘汰演算法,獲得匹配的資來源物件的對應檔案和位移量。然後將值讀取出來,通過JNI介面,拷貝回去。以上這些描述,並不能協助瞭解真實的實現細節,主要是為了促使大家對讀取資源的效率有一個比較直觀的認知。整個資源讀取的流程比較長,但是實現在C++中,可以預想,效率比Java高一些,開發人員,應該能夠根據自己的需求,決定是否將內容寫入資源檔中(還是寫在代碼中...),是不是需要自己稍微緩衝一下,諸如此類。
========================================
申請了自己的空間和網域名稱,總算有了家的感覺,有興趣的可以移步flyvenus.net,會同步更新相關內容,並提供更好的閱讀體驗。
========================================