UI繪製流程,讓無數安卓工程師無從下手?一篇文章就教你讀懂!
前言
在android當中對於UI體系當中往往我們會在繪製UI的時候碰到各種各樣的問題而不知道從何解決, 也有時需要開發更改自定義元件時,需要做自己的調整,或者是實現某個自定義特效時的思路不明確,想要達到去玩轉UI的最為基礎的部分,就是去全面的深入瞭解UI的繪製流程.所以接下來帶大家去進行全面分析UI整體的繪製體系.
思路:android程式啟動-→Activity載入並完成生命週期-→setContentView-→圖形繪製
疑惑:
1.Android程式是如何啟動,Activity生命週期如何呼叫?
2.在Activity onCreate當中我們的setContentView是如何將UI檔案載入?
3.UI是如何繪製的?
答案:
1.Android程式流程
眾所周知,我們的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狀態改變時,都會有對應的一個訊息傳送出去
而接收這裡,我能發現透過傳送時不同的狀態,這邊呼叫了不同的handlerXXXActivity方法

在這裡,我門貌似發現了Activity的生命週期的呼叫痕跡,那麼其實到此為止,我門可以得出一個結論,
Application執行的過程當中,對於Activity的操作,狀態轉變,其實實際上是透過Handler訊息機制來完成的,
Application當中只管去發, 由訊息機制負責呼叫,因為在main方法當中我門的Looper輪訓器是一直在進行輪訓的
而當我們在載入Activity的時候,當中呼叫了一個performLaunchActivity()方法,在這個中間我發現了我們onCreate的呼叫痕跡

也就是說,到目前為止我們能夠明白,整個Application載入Activity的整套流程是怎麼回事
那麼接下來我們需要關注的是,在onCreate當中我們所寫的setContentView到底幹了什麼
2.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其實做了兩件比較核心的事情,就是載入環境配置,和自己的佈局,那麼接下來我門需要考慮的事情就是,他到底怎麼畫到介面上的
3.UI是如何繪製的?
透過前面兩個章節,我門瞭解到,程式對於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
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952849/viewspace-2672837/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一篇文章教你如何讀懂 JMeter聚合報告引數!JMeter
- 靈魂畫師,Android繪製流程——Android高階UIAndroidUI
- Android UI繪製流程及原理AndroidUI
- Flutter之UI繪製流程二FlutterUI
- 車間數字化轉型無從下手,Smartbi來幫你
- 一篇文章讀懂SDP、RTMP、HLS、SIP、MMS
- 設計模式系列 · 無從下手的困惑 (一)設計模式
- 是什麼讓初級工程師走投無路?工程師
- 一篇文章讀懂阻塞,非阻塞,同步,非同步非同步
- 一篇文章讀懂瀏覽器渲染機制瀏覽器
- 一篇文章帶你讀懂Redis的哨兵模式Redis模式
- 繪製流程
- Android View繪製流程看這篇就夠了AndroidView
- 五分鐘學後端技術:一篇文章教你讀懂大資料技術棧!後端大資料
- 使用joinjs繪製流程圖(五)-流程圖繪製JS流程圖
- java工程師linux命令,這篇文章就夠了Java工程師Linux
- 封裝vue外掛,讀懂這遍文章就夠了封裝Vue
- 從螺絲釘到無名小卒-IC工程師何去何從工程師
- 一篇讀懂!遊戲音訊工業化流程拆解遊戲音訊
- 招聘:群控(雲控)開發工程師/安卓逆向開發工程師工程師安卓
- 一篇文章看懂安卓狀態列各種樣式安卓
- 準備程式設計/編碼面試無從下手?這10本好書值得一讀程式設計面試
- iOS UI繪製原理iOSUI
- CSS3繪製安卓系統機器人CSSS3安卓機器人
- View的繪製二:View的繪製流程View
- View 繪製流程分析View
- Dubbo 一篇文章就夠了:從入門到實戰
- 一篇文章教你如何製作二次元角色建模!二次元
- 審批流程圖怎樣繪製?繪製流程圖方法有哪些流程圖
- 安卓原生工程整合Flutter工程安卓Flutter
- 安卓架構文章合集安卓架構
- 自定義繪製鐘錶控制元件,這一篇就夠了控制元件
- 掌握 CORS 跨域請求,讀這一篇文章就夠了CORS跨域
- 流程圖製作: BPMN流程圖線上繪製流程圖
- 一招教你無阻塞讀寫 Golang channelGolang
- idou老師教你學Istio 27:解讀Mixer Report流程
- Linux系統下手把手完成無人值守安裝服務Linux
- 安卓程式設計師:如何用Espresso測試UI介面安卓程式設計師EspressoUI