在android當中對于UI體系當中往往我們會在繪制UI的時候碰到各種各樣的問題而不知道從何解決, 也有時需要開發更改自定義元件時,需要做自己的調整,或者是實作某個自定義特效時的思路不明确,想要達到去玩轉UI的最為基礎的部分,就是去全面的深入了解UI的繪制流程.是以接下來帶大家去進行全面分析UI整體的繪制體系.
android程式啟動--->Activity加載并完成生命周期--->setContentView--->圖形繪制
1.Android程式是如何啟動,Activity生命周期如何調用?
2.在Activity onCreate當中我們的setContentView是如何将UI檔案加載?
3.UI是如何繪制的?
衆所周知,我們的java程式想要開啟需要依賴于main方法,也就是我們的程式入口(主線程)進入,但是在我們日常開發android程式的過程當中我們并沒有發現main方法的存在,那麼android當中的是如何開始運作的?
熟悉的朋友們可能都知道在android當中存在一個叫做ActivityThread的類,這個類代表的是android當中的主線程,而在這個類當中我們看到了比較熟悉的main方法,那麼現在是否可以認為我們的android在打開app時是首先調用的是目前這個類的main,也就是此處為我們的啟動點
在此處可以看到Activity調用了一個attach()方法
在這裡我們可能首先要考慮的是getService拿出來的是什麼? 進去之後,我們會發現
在這個當中,裡面調用了的系統的ActivityManagerService這個服務,并且給出了一個Binder接口
那麼在這裡,我們可以聯想到,在android當中的binder通信機制,那麼實際上我們的ActivityManager是有系統服務所調用管理,并且通過在binder接口當中進行調用,這也是為什麼我們講Activity是跨程序通路的原因
那麼明白了這個時候能夠得到ActivityManager之後,我們接着回到attach當中繼續看下去, 這個時候會發現,我們調用了一個attachApplication方法(見圖2)這個方法又是幹嘛的? attachApplication在這裡的作用其實實際上是ActivityThread通過attach擷取到,然後将applciationThread将其關聯,把activity相關資訊存儲在applciationThread裡面,apllicationThread的類為activity的各種狀态做了相對應的準備工作
這個時候我們需要關注,ApplicationThread當中做了什麼? 當我們打開ApplicationThread中我們會看到一堆的schedle方法,這些方法的名稱其實就可以給我們表明,代表的是在執行Activity的某種狀态時調用的計劃執行方法
這時我們會看到一個scheduleLaunchActivity方法,表示計劃加載時調用的。這裡我門發現了一個很有意思的事情
這個上面我們會看到一個ActivityClientRecord對象,這個對象其實實際上就是我們的Activity,而且似乎每一個方法還幹了一件讓我們非常熟悉的一件事, 進行了一次sendMessage()将目前建立的Activity發送了出去
當走到這裡我們會發現最終我們調用的是Handler的消息通信機制,也就是說,在這裡我們可以總結一下,當Activity狀态改變時,都會有對應的一個消息發送出去。而接收這裡,我能發現通過發送時不同的狀态,這邊調用了不同的<code>handlerXXXActivity</code>方法
在這裡,我門貌似發現了Activity的生命周期的調用痕迹,那麼其實到此為止,我門可以得出一個結論,<code>Application</code>運作的過程當中,對于Activity的操作,狀态轉變,其實實際上是通過Handler消息機制來完成的,<code>Application</code>當中隻管去發, 由消息機制負責調用,因為在main方法當中我門的Looper輪訓器是一直在進行輪訓的,而當我們在加載Activity的時候,當中調用了一個<code>performLaunchActivity()</code>方法,在這個中間我發現了我們onCreate的調用痕迹
也就是說,到目前為止我們能夠明白,整個Application加載Activity的整套流程是怎麼回事? 那麼接下來我們需要關注的是,在onCreate當中我們所寫的setContentView到底幹了什麼
在onCreate當中我們往往會使用setContentView去進行設定我們自己的布局檔案或者view,那麼在這當中他到底是怎麼做的?通過觀察源碼,這個時候通過一系列線索我找到了最終的位置PhoneWindow類
這個時候我們會看到他做了兩個事情,一個是installDecor,另一個是inflate,這兩個後一個不難猜出他是在進行布局檔案的解析, 前面的我們認為她是在初始化某個東西
進來之後發現他初始化了兩個東西,一個叫做mDecor,一個叫做mContentParent
我們看到了mDecor是一個<code>DecorView</code>,mContentParent是一個<code>ViewGroup</code>
透過注釋的翻譯,其實我們就能很明确知道這兩個是用來幹嘛的
一個代表的是頂層view,一個用來裝他下面的視圖内容。 在接着往下看的時候,我門發現<code>generateLayout</code>方法當中,發現了在此處進行了大量的<code>requestFeature</code>的調用,也就是說,我們的<code>requestFeature</code>設定其實是在<code>setContentView</code>方法當中就開始了, 這也是為什麼我們自己要去<code>getWindow.requestFeature</code>時必須在<code>setContent</code>之前的原因
然後在下面我們會發現在做了一件事情
目前這裡竟然在加載布局檔案,并且生成了一個view, 但是好像貌似不是我門自己的。是以我們需要去探尋他到底加載了一個什麼東東?
這是我找到了一個比較有意思的元件,在這個上面我看到了一句這樣的注釋
這是一個螢幕的優化布局,具有最小的特征集啟用。通過注釋和一些資料分析, 得到了一個比較坑的結果。
這是<code>DecorView</code>預設的一個渲染,然後我門自己的布局都是渲染到她的<code>FrameLayout</code>上的。那麼在這裡我門現在能夠明白,<code>installDector</code>其實實際上是在初始化兩個視圖容器,然後加載系統的R資源及特征,産生了一個基本布局
那麼接着回到之前我門關注的另外一個方法mLayoutInflater.inflate(layoutResID, mContentParent);這個方法就比較好了解了。
這這段注釋上面我門就可以得到一個資訊
而具體流程就不貼代碼了給各位上一張圖
那麼在這裡我門就能夠明白,setContentView其實做了兩件比較核心的事情,就是加載環境配置,和自己的布局,那麼接下來我門需要考慮的事情就是,他到底怎麼畫到界面上的
通過前面兩個章節,我門了解到,程式對于activity生命周期的調用,以及我們的視圖資源的由來。這是我門需要找到的是我門的繪制起點在哪?
在ActivityThread啟動時, 我發現在加載handleLaunchActivity方法調用performLaunchActivity方法之後又調用了一個handleResumeActivity在這裡我發現了繪制流程的開始
通過前面的流程我門知道,onCreate之行完成之後,所有資源交給WindowManager保管。在這裡将我們的VIew交給了WindowManager,此處調用了addView
進入addView之後我們發現了一段這樣的代碼,他将視圖,和參數還有我門的一個ViewRoot對象都用了容器去裝在了起來,那麼在此處我門可以得出,是将所有的相關對象儲存起來:
mViews儲存的是View對象,DecorView
mRoots儲存和頂層View關聯的ViewRootImpl對象
mParams儲存的是建立頂層View的layout參數。
而WindowManagerGlobal類也負責和WMS通信
而在此時,有一句關鍵代碼root.setView,這裡是将我們的參數,和視圖同時交給了ViewRoot,那麼這個時候我們來看下ViewRoot當中的setView幹了什麼
終于在這裡讓我發現了讓我明白的一步
在這裡我門會看到view.assignParent的設定是this, 那麼也就是說在view當中parent其實實際上是ViewRoot
那麼在setContentView當中調用了一個setLayoutParams()是調用的ViewRoot的,而在ViewRoot當中發現了setLayoutParams和preformLayout對requestLayout方法的調用
在requestLayout當中發現了對scheduleTraversals方法的調用而scheduleTraversals當中調用了doTraversal的通路,最終通路到了performTraversals(),而在這個裡面我發現了整體的繪制流程的調目前裡面依次是用了
UI繪制先回去測量布局,然後在進行布局的擺放,當所有的布局測量擺放完畢之後,進行繪制。至此整體UI繪制過程我們就已經非常清楚了,我們可以根據這種繪制的流程來操作自己的自定義元件。
如果你覺得這篇内容對你還蠻有幫助,歡迎關注公衆号<code>小新聊Android</code> 如果你正在找工作, 那麼你需要一份 Android進階開發面試寶典(已收錄到Github)