理解Window和WindowManager

Window是一個抽象類页眯,它的具體實(shí)現(xiàn)是PhoneWindow瓣窄。創(chuàng)建一個Window是很簡單的事,只需要通過WindowManager即可完成索昂。WindowManager是外界訪問Window的入口,Window的具體實(shí)現(xiàn)位于WindowManagerService中扩借,WindowManager和WindowManagerService的交互是一個IPC過程椒惨。

一、Window和WindowManager

通過WindowManager添加Window:

將一個Button添加到屏幕為(100,300)的位置上

Flags參數(shù)表示W(wǎng)indow的屬性:

FLAG_NOT_FOCUSABLE

表示W(wǎng)indow不需要獲取焦點(diǎn)潮罪,也不需要接收各種輸入事件康谆,此標(biāo)記會同時(shí)啟用FLAG_NOT_TOUCH_MODAL,最終事件會直接傳遞給下層的具有焦點(diǎn)的Window。

FLAG_NOT_TOUCH_MODAL

在此模式下嫉到,系統(tǒng)會將當(dāng)前Window區(qū)域以外的單擊事件傳遞給底層的Window沃暗,當(dāng)前Window區(qū)域以內(nèi)的單擊事件則自己處理。這個標(biāo)記很重要何恶,一般來說都需要開啟此標(biāo)記孽锥,否則其他Window將無法收到單擊事件。

FLAG_SHOW_WHEN_LOCKED

開啟此模式可以讓W(xué)indow顯示在鎖屏的界面上。

type參數(shù)表示W(wǎng)indow的類型惜辑,Window有三種類型唬涧,分別是應(yīng)用Window、子Window和系統(tǒng)Window盛撑。應(yīng)用類Window對應(yīng)著一個Activity碎节。子Window不能單獨(dú)存在,需要附屬在特定的父Window之中抵卫,比如常見的Dialog狮荔。系統(tǒng)Window是需要聲明權(quán)限在能創(chuàng)建的Window,比如Toast和系統(tǒng)狀態(tài)欄介粘。

Window是分層的殖氏,每個Window都有對應(yīng)的z-ordered,層級大的會覆蓋在層級小的Window的上面碗短。

應(yīng)用Window的層級范圍時(shí)1~99受葛,子Window的層級范圍時(shí)1000~1999,系統(tǒng)Window的層級范圍是2000~2999,這些層級范圍對應(yīng)著WindowManager.LayoutParams的type參數(shù)

WindowManager所提供的功能很簡單偎谁,常用的只有三個方法总滩,即添加View、更新View和刪除View巡雨,這三個方法定義在ViewManager中闰渔,而WindowManager繼承了ViewManager。

二铐望、Window的內(nèi)部機(jī)制

每一個Window都對應(yīng)著一個View和一個ViewRootImpl冈涧,Window和View通過ViewRootImpl來建立聯(lián)系,因此Window并不是實(shí)際存在的正蛙,是以View的形式存在督弓。

2.1 Window的添加過程

Window的添加過程需要通過WindowManager的addView來實(shí)現(xiàn),WindowManager是一個接口乒验,它的真正實(shí)現(xiàn)是WindowManagerImplement類愚隧。其三大操作:

可以發(fā)現(xiàn),WindowManagerImpl并沒有直接實(shí)現(xiàn)Window的三大操作锻全,而是全部交給了WindowManagerGlobal來處理狂塘,WindowManagerGlobal以工廠的形式向外提供自己的實(shí)例。

WindowManagerGlobal的addView方法主要分為如下幾步:

1鳄厌、檢查參數(shù)是否合法荞胡,如果是子Window那么還需要調(diào)整一些布局參數(shù)

2、創(chuàng)建ViewRootImpl并將View添加到列表中

在上面的聲明中了嚎,mViews存儲的是所有Window所對應(yīng)的View泪漂,mRoots所存儲的是所有Window所對應(yīng)的ViewRootImpl,mParams存儲的是所有Window所對應(yīng)的布局參數(shù),而mDyingViews則存儲了那些正在被刪除的View對象窖梁,或者說是那些已經(jīng)被調(diào)用removeView方法但是刪除操作還未完成的Window對象赘风。在addView中通過如下方式將Window的一系列對象添加到列表中:

3、通過ViewRootImpl來更新界面并完成Window的添加過程

這個步驟由ViewRootImpl的setView方法來完成:

接著會通過WindowSession最終來完成Window的添加過程纵刘。

mWindowSession的類型是IWindowSession,它是一個Binder對象邀窃,真正的實(shí)現(xiàn)類是Session

在Session內(nèi)部會通過WindowManagerService來實(shí)現(xiàn)Window的添加:

2.2 Window的刪除過程

Window的刪除過程和添加過程一樣,都是先通過WindowManagerImpl后假哎,再進(jìn)一步通過WindowManagerGlobal來實(shí)現(xiàn)的瞬捕。下面是WindowManagerGlobal的removeView的實(shí)現(xiàn):

removeView通過findViewLocked來查找待刪除的View的索引,這個查找過程就是建立的數(shù)組遍歷舵抹,然后再調(diào)用removeViewLocked來做進(jìn)一步的刪除:

removeViewLocked是通過ViewRootImpl來完成刪除操作的肪虎。在WindowManager中提供了兩種刪除接口removeView和removeViewImmediate,它們分別表示異步刪除和同步刪除惧蛹。

removeView是由ViewRootImpl的die方法來完成扇救。而die方法只是發(fā)送了一個請求刪除的消息后就立刻返回了,這個時(shí)候View并沒有完成刪除操作香嗓,所以最好會將其添加到mDyingViews中迅腔,mDyingViews表示待刪除的View列表。

在de方法內(nèi)部只是做了簡單的判斷靠娱,如果是異步刪除沧烈,那么就發(fā)送一個MSG_DIE的消息,ViewRootImpl中的Handler會處理此消息并調(diào)用doDie方法像云,如果是同步刪除(立即刪除)锌雀,那么久不乏消息直接調(diào)用doDie方法,這就是兩種刪除方式的區(qū)別迅诬。在doDie內(nèi)部會調(diào)用dispatchDetachedFromWindow方法腋逆,這個方法主要做四件事:

1、垃圾回收相關(guān)的工作侈贷,比如清除數(shù)據(jù)和消息闲礼、移除回調(diào)

2、通過Session的remove方法刪除Window:mWindowSession.remove(mWindow)铐维,這同樣是一個IPC過程,最終會調(diào)用WindowManagerService的removeWindow方法

3慎菲、戴傲勇View的dispatchDetachedFromWindow方法嫁蛇,在內(nèi)部會調(diào)用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。當(dāng)View從window中移除時(shí)露该,會調(diào)用onDetachedFromWindow睬棚,可在這個方法內(nèi)部做一些資源回收的工作。

4、調(diào)用WindowManagerGlobal的doRemoveView方法刷新數(shù)據(jù)抑党,包警,包括mRoots、mParams以及mDyingViews底靠,需要將當(dāng)前Window所關(guān)聯(lián)的這三類對象從列表中刪除害晦。

2.3 Window的更新

查看WindowManagerGlobal的updateViewLayout方法:

首先需要更新View的LayoutParams并替換掉老的LayoutParams,接著在更新ViewRootImpl中的LayoutParams暑中。接著再更新ViewRootImpl中的LayoutParams壹瘟,這一步是通過ViewRootImpl的setLayoutParams方法來實(shí)現(xiàn)的。在ViewRootImpl中會通過scheduleTraversals方法來對View重新布局鳄逾,包括測量稻轨、布局、重繪這三個過程雕凹。在通過WindowSession來更新Window的視圖殴俱,這個過程是有WindowManagerService的relayoutWindow來具體實(shí)現(xiàn)。


三枚抵、Window的創(chuàng)建過程

3.1 Activity的Window創(chuàng)建過程

在Activity的啟動過程中线欲,會調(diào)用attach方法,這個方法會創(chuàng)建Activity所屬的Window對象并為其設(shè)置回調(diào)接口俄精,Window對象的創(chuàng)建時(shí)通過PolicyManager的makeNewWindow方法實(shí)現(xiàn)的询筏,由于Activity實(shí)現(xiàn)了Window的Callback接口,所以當(dāng)Window接收到外界的狀態(tài)改變時(shí)就會回調(diào)Activity的方法竖慧。代碼如下:

從上面可看出嫌套,Activity的Window是通過PolicyManager的一個工廠方法來創(chuàng)建的,但是從PolicyManager的類名可以看出圾旨,他不是一個普通類踱讨,它是一個策略類。PolicyManager中實(shí)現(xiàn)的幾個工廠方法全部在策略接口中IPolicy中聲明砍的,IPolicy的定義如下:

而方法makeNewWindow實(shí)現(xiàn)如下:

分析Activity的視圖是怎么附屬在Window上:

由于Activity的視圖由setContentView方法提供痹筛,我們只需要看setContentView方法的實(shí)現(xiàn)即可:

可看出Activity將具體實(shí)現(xiàn)交給了window處理,而Window的具體實(shí)現(xiàn)是PhoneWindow廓鞠,所以只需看PhoneWindow的相關(guān)邏輯即可帚稠,其setContentView方法遵循如下步驟:

1、如果沒有DecorView床佳,那么就創(chuàng)建它

DecorView是Activity的頂級View滋早,一般來說它的內(nèi)部包含標(biāo)題欄和內(nèi)部欄,但是這個會隨著主題的變換而發(fā)生改變砌们,不管怎么樣杆麸,內(nèi)容欄是一定要存在的搁进,并且內(nèi)容來具體固定的id,那就是“content”昔头,它的完整id是android.R.id.content饼问。DecorView的創(chuàng)建過程由installDecor方法來完成,在內(nèi)部會通過generateLayout方法來直接創(chuàng)建DecorView揭斧。

為了初始化DecorView的結(jié)構(gòu)莱革,PhoneWindow還需通過generateLayout方法來加載具體的布局文件到DecorView中,具體的布局文件和系統(tǒng)版本以及主題有關(guān):

其中ID_ANDROID_CONTENT的定義如下未蝌,這個id所對應(yīng)的ViewGroup就是mContentParent:

2驮吱、將View添加到DecorView的mContentParent中

直接將Activity的視圖添加到DecorView的mContentParent中即可:沒LayoutInflater.inflate(layoutResID,MContentParent)。

3萧吠、回調(diào)Activity的onContentChanged方法通知Activity視圖已經(jīng)發(fā)生改變

可以直接在Activity的onContentChanged方法是個空實(shí)現(xiàn)左冬,可在子Activity中處理這個回調(diào):

經(jīng)過上面三個步驟,activity的布局文件已經(jīng)成功添加到了DecorView的mContentParent中纸型,但是這個時(shí)候DecorView還沒有被WindowManager正式添加到Window中拇砰。只有在ActivityThread的handleResumeActivity方法中,首先會調(diào)用Aactivity的onResume方法狰腌,接著會調(diào)用Activity的makeVisible()除破,正是在makeVisible方法中,DecorView真正完成了添加和顯示這兩個過程:

3.2 Dialog的Window創(chuàng)建過程

Dialog的Window創(chuàng)建過程和Activity類似琼腔,有如下步驟:

1瑰枫、創(chuàng)建Window

Dialog中Window的創(chuàng)建同樣是通過PolicyManager的makeNewWindow方法來完成的:

2、初始化DecorView并將Dialog的視圖添加到DecorView中

3丹莲、將DecorView添加到Window中并顯示

在Dialog的show方法中光坝,會通過WindowManager將DecorView添加到Window中,如下所示:

當(dāng)Dialog被關(guān)閉時(shí)甥材,它會通過WindowManager來移除DecorView:mWindowManager.removeViewImmediate(mDecor).

普通的Dialog有一個特殊之處盯另,那就是必須采用Activity的Context,如果采用Application的Context洲赵,那么就會報(bào)錯鸳惯。

3.3 Toast的Window創(chuàng)建過程

Toast也是基于Window來實(shí)現(xiàn)的,但是由于Toast具有定時(shí)取消這一功能叠萍,所以系統(tǒng)采用了Handler芝发。

Toast內(nèi)部有兩類IPC過程,第一類是Toast訪問NotificationManagerService(NMS)苛谷,第二類是NotificationManagerService回調(diào)Toast里的TN接口后德。

Toast屬于系統(tǒng)Window,它內(nèi)部的視圖由兩種方式指定抄腔,一種是系統(tǒng)默認(rèn)的樣式瓢湃,另一種是通過setView方法來指定一個自定義View,不管如何赫蛇,他們都對應(yīng)Toast的一個View類型的內(nèi)部成員mNextView绵患。其show與cancel方法實(shí)現(xiàn)如下:

顯示和隱藏Toast都需要通過NMS來實(shí)現(xiàn),由于NMS運(yùn)行在系統(tǒng)的進(jìn)程中悟耘,所以只能通過遠(yuǎn)程調(diào)用的方式來顯示和隱藏Toast落蝙。而TN這個類,它是一個Binder類暂幼,在Toast和NMS進(jìn)行IPC的過程中筏勒,當(dāng)NMS處理Toast的顯示或隱藏請求時(shí)會跨進(jìn)程回調(diào)TN中的方法,這個時(shí)候由于TN運(yùn)行在Binder線程中旺嬉,所以需要通過Handler將其切換到當(dāng)前線程中管行。

Toast的顯示過程:

NMS的enqueueToast方法的第一個參數(shù)表示當(dāng)前應(yīng)用的包名,第二個參數(shù)tn表示遠(yuǎn)程回調(diào)邪媳,第三個參數(shù)表示Toast的時(shí)長捐顷。enqueueToast首先將Toast請求封裝為ToastRecord對象并將其添加到一個名為mToastQueue的隊(duì)列中。

當(dāng)ToastRecord被添加到mToastQueue中后雨效,NMS就會通過showNextToastLocked方法來顯示當(dāng)前的Toast迅涮。其中,Toast的顯示是由TsatRecord的callback來完成的徽龟,這個callback實(shí)際上就是Toast中的TN對象的遠(yuǎn)程Binder叮姑,通過callback來訪問TN中的方法是需要跨進(jìn)程來完成的,最終被調(diào)用的TN中的方法虎運(yùn)行在發(fā)起Toast請求的應(yīng)用的Binder線程池中据悔。

Toast顯示以后传透,NMS還會通過scheduleTimeoutLocked方法來發(fā)送一個延時(shí)消息,具體的延時(shí)取決于Toast的時(shí)長:

在上面額代碼證屠尊,LONG_DELAY是3.5s旷祸,而SHORT_DELAY是2s。延遲相應(yīng)的時(shí)間后讼昆,NMS會通過cancelToastLocked方法來隱藏Toast并將其從mToastQueue中移除托享,這個時(shí)候如果mToastQueue中還有其他Toast,那么NMS就繼續(xù)顯示其他Toast浸赫。

Toast的隱藏也是通過ToastRecord的callback來完成:

Toast的顯示和隱藏過程實(shí)際上是通過Toast中的TN這個類來實(shí)現(xiàn)的闰围,它有兩個方法show和hide,分別對應(yīng)Toast的顯示和隱藏既峡。由于這兩個方法是被NMS以跨進(jìn)程的方式調(diào)用羡榴,因此它們運(yùn)行在線程池中:

mShow和mHide是兩個Runnable,分別調(diào)用了handleShow和handleHide方法运敢,TN的handleShow中會將Toast的視圖添加到Window中:

而NT的handleHide中會將Toast的視圖從Window中移除:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末校仑,一起剝皮案震驚了整個濱河市忠售,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迄沫,老刑警劉巖稻扬,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異羊瘩,居然都是意外死亡泰佳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門尘吗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逝她,“玉大人,你說我怎么就攤上這事睬捶∏穑” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵侧戴,是天一觀的道長宁昭。 經(jīng)常有香客問我,道長酗宋,這世上最難降的妖魔是什么积仗? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蜕猫,結(jié)果婚禮上寂曹,老公的妹妹穿的比我還像新娘。我一直安慰自己回右,他們只是感情好隆圆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翔烁,像睡著了一般渺氧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹬屹,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天侣背,我揣著相機(jī)與錄音,去河邊找鬼慨默。 笑死贩耐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的厦取。 我是一名探鬼主播潮太,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了铡买?” 一聲冷哼從身側(cè)響起更鲁,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奇钞,沒想到半個月后岁经,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛇券,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了樊拓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纠亚。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筋夏,靈堂內(nèi)的尸體忽然破棺而出蒂胞,到底是詐尸還是另有隱情,我是刑警寧澤条篷,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布骗随,位于F島的核電站,受9級特大地震影響赴叹,放射性物質(zhì)發(fā)生泄漏鸿染。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一乞巧、第九天 我趴在偏房一處隱蔽的房頂上張望涨椒。 院中可真熱鬧,春花似錦绽媒、人聲如沸蚕冬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽囤热。三九已至,卻和暖如春获三,著一層夾襖步出監(jiān)牢的瞬間旁蔼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工石窑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牌芋,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓松逊,卻偏偏與公主長得像躺屁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子经宏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容