在android當中對於UI體系當中每每咱們會在繪製UI的時候碰到各類各樣的問題而不知道從何解決, 也有時須要開發更改自定義組件時,須要作本身的調整,或者是實現某個自定義特效時的思路不明確,想要達到去玩轉UI的最爲基礎的部分,就是去全面的深刻了解UI的繪製流程.因此接下來帶你們去進行全面分析UI總體的繪製體系.java
思路:android程序啓動-→Activity加載並完成生命週期-→setContentView-→圖形繪製android
1.Android程序是如何啓動,Activity生命週期如何調用?git
2.在Activity onCreate當中咱們的setContentView是如何將UI文件加載?github
答案:面試
衆所周知,咱們的java程序想要開啓須要依賴於main方法,也就是咱們的程序入口(主線程)進入,可是在咱們平常開發android程序的過程中咱們並無發現main方法的存在,那麼android當中的是如何開始運行的?算法
熟悉的朋友們可能都知道在android當中存在一個叫作ActivityThread的類,這個類表明的是android當中的主線程,而在這個類當中咱們看到了比較熟悉的main方法,那麼如今是否能夠認爲咱們的android在打開app時是首先調用的是當前這個類的main,也就是此處爲咱們的啓動點性能優化
在此處能夠看到Activity調用了一個attach()方法app
在這裏咱們可能首先要考慮的是getService拿出來的是什麼?ide
進去以後,咱們會發現oop
在這個當中,裏面調用了的系統的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狀態改變時,都會有對應的一個消息發送出去
而接收這裏,我能發現經過發送時不一樣的狀態,這邊調用了不一樣的handlerXXXActivity方法
在這裏,我門貌似發現了Activity的生命週期的調用痕跡,那麼其實到此爲止,我門能夠得出一個結論,
Application運行的過程中,對於Activity的操做,狀態轉變,其實其實是經過Handler消息機制來完成的,
Application當中只管去發, 由消息機制負責調用,由於在main方法當中我門的Looper輪訓器是一直在進行輪訓的
而當咱們在加載Activity的時候,當中調用了一個performLaunchActivity()方法,在這個中間我發現了咱們onCreate的調用痕跡
也就是說,到目前爲止咱們可以明白,整個Application加載Activity的整套流程是怎麼回事
那麼接下來咱們須要關注的是,在onCreate當中咱們所寫的setContentView到底幹了什麼
在onCreate當中咱們每每會使用setContentView去進行設置咱們本身的佈局文件或者view,那麼在這當中他究竟是怎麼作的?經過觀察源碼,這個時候經過一系列線索我找到了最終的位置PhoneWindow類
這個時候咱們會看到他作了兩個事情,一個是installDecor,另外一個是inflate,這兩個後一個不難猜出他是在進行佈局文件的解析, 前面的咱們認爲她是在初始化某個東西
進來以後發現他初始化了兩個東西,一個叫作mDecor,一個叫作mContentParent
咱們看到了mDecor是一個DecorView
mContentParent是一個ViewGroup
透過註釋的翻譯,其實咱們就能很明確知道這兩個是用來幹嗎的
// This is the view in which the window contents are placed. It is either(這是放置窗口內容的視圖)
// mDecor itself, or a child of mDecor where the contents go.(它要麼是mDecor自己,要麼是mDecor的子類的內容。)
//This is the top-level view of the window, containing the window decor.(這是在窗口當中的頂層View,包含窗口的decor)
一個表明的是頂層view,一個用來裝他下面的視圖內容
在接着往下看的時候,我門發現,generateLayout方法當中,發現了在此處進行了大量的requestFeature的調用,也就是所,咱們的requestFeature
而後在下面我門會發如今作了一件事情,
當前這裏居然在加載佈局文件,而且生成了一個view, 可是好像貌似不是我門本身的
因此咱們須要去探尋他到底加載了一個什麼東東?
這是我找到了一個比較有意思的組件,
在這個上面我看到了一句這樣的註釋
//This is an optimized layout for a screen, with the minimum set of features
enabled.
這是一個屏幕的優化佈局,具備最小的特徵集啓用。
經過註釋和一些資料分析, 獲得了一個比較坑的結果。
這是DecorView默認的一個渲染,而後我門本身的佈局都是渲染到她的FrameLayout上的
那麼在這裏我門如今可以明白,installDector其實其實是在初始化兩個視圖容器,而後加載系統的R資源及特徵,產生了一個基本佈局
那麼接着回到以前我門關注的另一個方法mLayoutInflater.inflate(layoutResID, mContentParent);
這個方法就比較好理解了,
這這段註釋上面我門就能夠獲得一個信息
//Inflate a new view hierarchy from the specified xml resource.(從指定的視圖當中獲取試圖的層次結構,意思就是,如今在加載本身的資源)
而具體流程就不貼代碼了給各位上一張圖
那麼在這裏我門就可以明白,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繪製過程咱們就已經很是清楚了。
咱們能夠根據這種繪製的流程來操做本身的自定義組件。
BATJ、字節跳動面試專題,算法專題,高端技術專題,混合開發專題,java面試專題,Android,Java小知識,到性能優化.線程.View.OpenCV.NDK等已經上傳到了的個人GitHub
你們點擊個人GitHub地址:https://github.com/Meng997998/AndroidJX點下star一塊兒學習