技術(shù)管理篇5一技術(shù)演變史(2)

昨天我們聊了UI繪制的設(shè)計模式,通過消息機(jī)制,優(yōu)雅的隔離了操作系統(tǒng)和各個軟件實現(xiàn)之間的耦合。今天我們繼續(xù)聊一下組件設(shè)計模式,怎么讓我們更進(jìn)一步擺脫大部分繪制的工作,專注在業(yè)務(wù)本身。

讓我們以安卓的組件體系為例,下圖是安卓UI組件體系的邏輯圖。


圖1-安卓UI組件關(guān)系圖(注:圖來自于網(wǎng)絡(luò))

篇幅有限,我們只集中聊整體的架構(gòu),其中的細(xì)節(jié),大家可以去查看源代碼繼續(xù)研究。我們可以看到,按照面向?qū)ο蟮乃悸烦霭l(fā),每個界面上的繪制單元我們可以抽象成一個View類,View類描繪了每個UI組件基本的方法和屬性,最基本的方法有三個,一個是要能繪制自己的OnDraw方法,一個是要能計算占位大小的onMeasure方法,最后一個是接收UI事件消息的方法。ViewGroup是組合控件的抽象類,他描述了組件之間的父子關(guān)系。

通過這樣的方式,我們就可以把UI展現(xiàn)抽象成一個View的樹狀結(jié)構(gòu),在安卓當(dāng)中,這個樹頂就是ContentView,如下圖所示。


圖2-安卓UI結(jié)構(gòu)圖(注:圖來自于網(wǎng)絡(luò))

ContentView中就會有多個子View,子View還可能再有多個子View,就形成了樹狀結(jié)構(gòu)。通過這種方式,我們就可以把上一篇中講到的UI主線程復(fù)雜邏輯隱藏掉。我們可以在安卓Window抽象類中來實現(xiàn)UI主線程Loop(這里簡化了很多細(xì)節(jié)),當(dāng)設(shè)定ContentView后,UI主線程Loop就會遍歷ContentView的樹,依次調(diào)用View的計算占位大小的方法,View再調(diào)用自己的子View,從而完成整個ContentView的Layout布局計算,布局計算完成后,再依次調(diào)用子View的繪制方法完成整個UI的繪制。當(dāng)有UI事件發(fā)生的時候,UI主線程Loop會根據(jù)子View的占位和顯示層級來判斷哪個View來接收這個消息,View接收到消息后,會根據(jù)情況選擇是否向父View傳遞消息。

我們可以看到,通過這個過程,就將復(fù)雜的UI主線程徹底隱藏起來。我們跟UI的交互,也變成了跟各個控件View的交互,使得事情變得簡單起來,我們只需要專注業(yè)務(wù)代碼的編寫就好。當(dāng)然,還是有一個漏洞無法隱藏,就是業(yè)務(wù)線程代碼是不能跨線程直接訪問View的,我們來看看安卓是怎么解決的。

如下圖,安卓通過Handler、MessageQueen機(jī)制來實現(xiàn)跨線程對View的訪問。下面我們來詳細(xì)說說。


圖3-安卓Handler機(jī)制(注:圖來自網(wǎng)絡(luò))

圖中的Looper就是我們所說的UI主線程Loop,他負(fù)責(zé)和操作系統(tǒng)打交道,完成UI繪制和事件分發(fā)。這里請允許我簡化來說,涉及到安卓和底層Linux的協(xié)作機(jī)制,我們先略過。這個Looper會一直監(jiān)聽MessageQueen,接收其他業(yè)務(wù)線程對UI的操作消息。那其他業(yè)務(wù)線程是如何把消息發(fā)送到Queen里呢,這就通過Handler來完成。Handler如何Looper聯(lián)系起來的呢,答案是ThreadLocal。

我們來串一下整個過程,首先,我們在UI主線程里創(chuàng)建Handler對象,切記要在主線程里創(chuàng)建,因為這時候,Handler會在當(dāng)前線程的ThreadLocal里獲取Looper對象。如果在業(yè)務(wù)線程中創(chuàng)建Handler對象,就沒法和Looper對象建立關(guān)聯(lián)了。

然后,當(dāng)我們在業(yè)務(wù)線程里,對View進(jìn)行操作的時候,我們可以給Handler發(fā)送消息就可以。此時的Handler在創(chuàng)建的時候就拿到了UI主線程Looper,他就可以往MessageQueen中放入消息了。

到此,安卓整個UI組件機(jī)制我們就說完了,當(dāng)然,其間很多細(xì)節(jié)我們略過了,有興趣的朋友可以再去詳細(xì)研究。其實不光在安卓中,其他的語言,比如.Net、Java、IOS等,都有類似的實現(xiàn)。平時大多數(shù)的控件就足夠我們使用,如果有特殊的需求,我們還可以自己繼承View,對組件做擴(kuò)展。組件化的封裝也帶來可視化設(shè)計的可能,拖拽的方式極大了提升了界面編寫的效率。你看好的設(shè)計模式就是如此優(yōu)雅,歷久彌新,始終在繼承和演化中。

另外,我們也可以看到,UI的嵌套層次越少,繪制效率就越高。尤其是不同的Layout組合使用的時候,某個子View的大小改變,因為需要重新排版Layout,可能會導(dǎo)致整個父View的重新繪制,如果層次過于復(fù)雜,這就會造成整個界面的卡頓現(xiàn)象。當(dāng)然,在性能足夠的PC上可能沒有這么嚴(yán)重,但是,手機(jī)上會特別突出。安卓其實也提供了不少工具讓我們排查這類問題,大家可以繼續(xù)去研究。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容