一耙饰、View繪制流程
Android繪制流程詳盡版
AndroidView繪制精簡(jiǎn)版
ViewRootImpl和繪制流程
Android View繪制流程
主流程
ViewRootImpl 是連接 WindowManager 和 DecorView 的紐帶舍败,測(cè)量、放置和繪制三大流程都是通過(guò) ViewRootImpl 實(shí)現(xiàn)的。
在 ActivityThread 的 handleResumeActivity() 方法中纸型,會(huì)調(diào)用 WindowManager 的 addView() 方法蘸泻,而具體添加 DecorView 的操作是在 WindowManagerGlobal 中。(WindowManager是個(gè)接口)
在 WindowManagerGlobal 的 addView() 方法中脱篙,會(huì)把 DecorView 添加到 Window 中娇钱,同時(shí)會(huì)創(chuàng)建 ViewRootImpl ,并調(diào)用 ViewRootImpl 的 setView() 方法 把 ViewRootImpl 和 DecorView 關(guān)聯(lián)起來(lái)绊困。
View 的繪制流程是從 ViewRootImpl 的 performTraversals() 方法開(kāi)始的文搂,它經(jīng)過(guò)測(cè)量(measure)、放置(layout)和繪制(draw)三個(gè)過(guò)程才能把一個(gè) View 繪制出來(lái)秤朗,measure() 方法用于測(cè)量 View 的寬高煤蹭,layout() 用于確定 View 在父容器中的放置位置,draw() 負(fù)責(zé)做具體的繪制操作川梅。
ViewRootImpl
要討論View的繪制流程疯兼,必須要知道一個(gè)類(lèi)ViewRootImpl.
ViewRootImpl是連接WindowManager和DecorView的紐帶。繪制流程中的測(cè)量(measure)贫途、布局(layout)和繪制(draw)都是通過(guò)ViewRootImpl實(shí)現(xiàn)的吧彪。
ViewRootImpl創(chuàng)建過(guò)程
ActivityThread # handleResumeActivity中調(diào)用WindowManager # addView,而WindowManager只是繼承ViewManager的一個(gè)接口丢早,其實(shí)現(xiàn)在WindowManagerImpl中姨裸。
WindowManagerImpl # addView中調(diào)用WindowManagerGlobal # addView秧倾。其內(nèi)部初始化了ViewRootImpl,并通過(guò)ViewRootImpl # setView()方法將view和ViewRootImpl關(guān)聯(lián)起來(lái)。
在decorView與ViewRootImpl建立關(guān)聯(lián)后傀缩,ViewRootImpl類(lèi)的requestLayout方法會(huì)被調(diào)用(上面的setView中調(diào)用)那先。
調(diào)用scheduleTraversals方法來(lái)調(diào)度一次完整的繪制流程,方法內(nèi)部通過(guò)內(nèi)存屏障保證繪制View的優(yōu)先級(jí)最高赡艰。
具體Api調(diào)用
ActivityThread:handleResumeActivity(該方法內(nèi)使用Context.getWindowManager創(chuàng)建WindowManager對(duì)象)→
WindowManager:addView(該方法內(nèi)WindowManager委托代理給一個(gè)WindowManagerGLobal對(duì)象)→
WindowManagerGLobal:addView(該方法內(nèi)創(chuàng)建了ViewRootImpl對(duì)象)→
ViewRootImpl:setView→requestLayout→scheduleTraversals→doTraversal→performTraversals(最終到達(dá)繪制的入口)
其中從WindowManager.addView開(kāi)始就是Activity創(chuàng)建Window的過(guò)程售淡,最終在ViewRootImpl對(duì)象的performTraversals中完成View的繪制(一個(gè)Window對(duì)象對(duì)應(yīng)了一個(gè)ViewViewRootImpl對(duì)象也對(duì)應(yīng)了一個(gè)View對(duì)象,即DecorView)
performTraversals()是繪制的入口慷垮,它依次調(diào)用performMeasure()揖闸、performLayout()和performDraw()三個(gè)方法,三個(gè)方法內(nèi)部分別調(diào)用了DecorView的measure()料身、layout()和draw方法汤纸。
其他:
1.getWidth()和getMeasuredWidth()的區(qū)別
getMeasuredWidth()、getMeasuredHeight()必須在onMeasure之后使用才有效)
getMeasuredWidth() 的取值最終來(lái)源于 setMeasuredDimension() 方法調(diào)用時(shí)傳遞的參數(shù)芹血;
getWidth()與getHeight()方法必須在layout(int l, int t, int r, int b)執(zhí)行之后才有效
getWidth()返回的是贮泞,mRight - mLeft,mRight幔烛、mLeft 變量分別表示 View 相對(duì)父容器的左右邊緣位置啃擦;
2.View.post(runnable)原理
利用Handler通信機(jī)制,發(fā)送一個(gè)Runnable到MessageQueue中说贝,當(dāng)View布局處理完成時(shí)议惰,自動(dòng)發(fā)送消息,通知UI進(jìn)程乡恕。借此機(jī)制言询,巧妙獲取View的高寬屬性,代碼簡(jiǎn)潔傲宜,相比ViewTreeObserver監(jiān)聽(tīng)處理运杭,還不需要手動(dòng)移除觀察者監(jiān)聽(tīng)事件。
3.requestLayout()的作用
requestLayout()也可以達(dá)到重繪view的目的函卒,但是與invalidate和postInvalidate不同辆憔,它會(huì)先調(diào)用onLayout()重新排版,再調(diào)用ondraw()方法报嵌。
當(dāng)view確定自身已經(jīng)不再適合現(xiàn)有的區(qū)域時(shí)虱咧,該view本身調(diào)用這個(gè)方法要求parent view(父類(lèi)的視圖)重新調(diào)用他的onMeasure、onLayout來(lái)重新設(shè)置自己位置锚国。特別是當(dāng)view的LayoutParams發(fā)生改變腕巡,并且它的值還沒(méi)能應(yīng)用到view上時(shí),這時(shí)候適合調(diào)用這個(gè)方法requestLayout()
4.Window&View
Window&View
①.window是什么血筑?
是所有Viewde直接管理者绘沉,任何視圖都通過(guò)window呈現(xiàn)煎楣。Activity的setContentView底層都通過(guò)Window完成。
-window是一個(gè)抽象類(lèi)车伞,唯一實(shí)現(xiàn)類(lèi)是PhoneWindow择懂;Window并不是實(shí)際存在的,而是以View的形式存在另玖。
-創(chuàng)建Window需要通過(guò)WindowManager創(chuàng)建
-實(shí)際使用中無(wú)法直接訪問(wèn)Window困曙,必須通過(guò)WindowManager
-Window具體實(shí)現(xiàn)位于WindowManagerService中,WindowManager和WindowManagerService的交互是通過(guò)IPC完成(PC是Inter-Process Communication的縮寫(xiě)日矫,含義為進(jìn)程間通信或者跨進(jìn)程通信赂弓,是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過(guò)程,這里指的是Binder)
-Window和View通過(guò)ViewRootImpl建立聯(lián)系
-WMS把所有的用戶(hù)消息發(fā)給View/ViewGroup,但是在View/ViewGroup處理消息的過(guò)程中哪轿,有一些操作是公共的, Window把這些公共行為抽象出來(lái), 這就是Window。
②.Window類(lèi)型
FrameWork定義了三種窗口類(lèi)型翔怎,三種類(lèi)型定義在WindowManager,通過(guò)LayoutParams.type設(shè)置窃诉。
-應(yīng)用窗口,對(duì)應(yīng)一個(gè)Activity赤套,每個(gè)Activity都包含一個(gè)Window對(duì)象飘痛。加載Activity由AmS完成,創(chuàng)建一個(gè)應(yīng)用窗口只能在Activity內(nèi)部完成(層級(jí)1~99)容握。
-子窗口宣脉,必須依附于任何類(lèi)型的父窗口(層級(jí)1000~1999)。
-系統(tǒng)窗口剔氏,不需要對(duì)應(yīng)任何Activity塑猖,如:狀態(tài)欄,導(dǎo)航欄,普通應(yīng)用程序不能創(chuàng)建系統(tǒng)窗口谈跛,必須要有系統(tǒng)應(yīng)用權(quán)限.(層級(jí)2000~2999)羊苟。
int值越大層位置越靠上
如下圖:
3.WindowManger的功能
主要就三個(gè):添加、更改感憾、刪除View
WindowManger是一個(gè)接口蜡励,ViewManager是其父接口,WindowMangerImpl是其實(shí)現(xiàn)類(lèi)阻桅。
4.Activity的setContentView流程
Activity#setContentView()實(shí)際通過(guò)getWindow().setContentView()交由PhoneWindow處理凉倚。
PhoneWindow中主要做兩件事:
①當(dāng)mDecor為空的時(shí)候,通過(guò)installDecor()初始化DecorView(mDecor)【DecoreView本質(zhì)就是一個(gè)FrameLayout嫂沉,是Activity中的頂級(jí)View】
②generateLayout()實(shí)際就是獲取布局的父容器(mContentParent), 然后通過(guò)inflate將我們的setContentView傳入的View或者layout布局文件填充到這個(gè)mContentParent稽寒,而這個(gè)父容器mContentParent實(shí)際上就是DecorView的id為R.id.content的子容器。
5.Activity输瓜、Window瓦胎、DecorView芬萍、View之間的關(guān)系
DecorView 把屏幕劃分為了兩個(gè)區(qū)域:一個(gè)是 TitleView,也就是ActionBar或者TitleBar,一個(gè)是 ContentView搔啊,而我們平時(shí)在 Xml 文件中寫(xiě)的布局正好是展示在 ContentView 中柬祠。
6.DecorView何時(shí)被WindowManager添加到Window中?
ActivityThread的handleResumeActivity方法中负芋,首先會(huì)調(diào)用Activity的onResume方法漫蛔,接著調(diào)用Activity的makeVisible()方法
makeVisible()中通過(guò)WindowManager.addView()完成了DecorView的添加和顯示兩個(gè)過(guò)程
7.Dialog的創(chuàng)建過(guò)程:
//TODO
8.Window常見(jiàn)FLAG屬性
這些屬性有幾個(gè)還有很重要的,比如:
FLAG_KEEP_SCREEN_ON: 屏幕常亮旧蛾,可以在播放視頻的時(shí)候設(shè)置這個(gè)參數(shù)
FLAG_FULLSCREEN: 沉浸式全屏
FLAG_SECURE:禁止截屏
FLAG_SHOW_WHEN_LOCKED:窗口在鎖屏窗口上顯示
Android事件分發(fā)機(jī)制
https://cloud.tencent.com/developer/article/1601306