一、Window和WindowManager
Android中所有視圖都是通過Window來呈現(xiàn)的窖逗,Activity、Dialog餐蔬、Toast的視圖實(shí)際上都是附加在Window上的碎紊,因此Window實(shí)際上是View的直接管理者。
Window有幾種屬性樊诺,使用Flags參數(shù)表示仗考,例如FLAG_NOT_FOCUSABLE表示不需要獲取焦點(diǎn),F(xiàn)LAG_NOT_TOUCH_MODAL表示W(wǎng)indow區(qū)域以外的點(diǎn)擊事件傳遞給底層的Window词爬,當(dāng)前區(qū)域事件則自己處理(一般都要開啟這個(gè)Flag)秃嗜,F(xiàn)LAG_SHOW_WHEN_LOCKED可鎖屏顯示。
Window是分層的顿膨,層級(jí)大的會(huì)覆蓋層級(jí)小的锅锨,應(yīng)用Window < 子Window < 系統(tǒng)Window層級(jí)。
WindowManager是外界訪問Window的入口恋沃,Window的具體實(shí)現(xiàn)是位于WindowManagerService中必搞,WindowManager和WindowManagerService交互是IPC過程。
二囊咏、Window的內(nèi)部機(jī)制
1. Window和View及ViewRoot的關(guān)系
Window 是一個(gè)抽象的概念恕洲,每一個(gè)Window都對(duì)應(yīng)一個(gè)View和一個(gè)ViewRootImpl,Window和View通過ViewRootImpl來建立聯(lián)系梅割。
在Window添加View的過程中霜第,為其內(nèi)部創(chuàng)建了一個(gè)ViewRootImpl對(duì)象,負(fù)責(zé)繪制顯示各個(gè)子View户辞,而ViewRootImpl中調(diào)用的setView()方法會(huì)輾轉(zhuǎn)調(diào)用performTraversals()方法從而實(shí)現(xiàn)View的一系列繪制流程泌类。
操作Window的三個(gè)方法:
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
創(chuàng)建并顯示W(wǎng)indow只需向其添加View,刪除Window只需刪除里面的View即可咆课。
2. Window的添加
WindowManager的addView()全部委托給了WindowManagerGlobal來實(shí)現(xiàn)末誓,做了如下工作:
- 檢測(cè)參數(shù)是否合法扯俱,對(duì)于子Window調(diào)整布局參數(shù)。
- 創(chuàng)建ViewRootImpl并將View添加到列表中喇澡。
- 通過ViewRootImpl更新界面迅栅,其setView()調(diào)用performTravelsals()會(huì)繪制整個(gè)View,接著通過WindowSession與WindowManagerService進(jìn)行IPC調(diào)用來實(shí)現(xiàn)Window的添加晴玖。
3. Window的刪除
全部委托給了WindowManagerGlobal來實(shí)現(xiàn)读存。
1.查找待刪除的View索引
2.通過ViewRootImpl完成接下來的刪除操作,對(duì)于異步刪除的情況呕屎,ViewRootImpl發(fā)送一個(gè)MSG_DIE消息让簿,由ViewRootImpl的Handler處理此消息來刪除。
3.刪除主要做垃圾回收相關(guān)秀睛,通過Session來IPC調(diào)用WindowManagerService的刪除方法移除Window尔当,最后由WindowManagerGlobal刷新數(shù)據(jù)。
4. Window更新
全部委托給了WindowManagerGlobal來實(shí)現(xiàn)蹂安。
首先更新View的LayoutParams并替換老的LayoutParams椭迎,再更新ViewRootImpl的LayoutParams,此時(shí)會(huì)通過scheduleTraversals()對(duì)View重新布局田盈。最后通過WindowSession的IPC更新Window的視圖畜号。
三、Window的創(chuàng)建
1. Activity的Window創(chuàng)建及與View的聯(lián)系建立
attach()
在創(chuàng)建Activity的performLaunchActivity()方法中會(huì)調(diào)用attach()方法創(chuàng)建PhoneWindow并為其關(guān)聯(lián)一系列上下文環(huán)境變量允瞧。
setContentView()
接下來是Window如何添加View简软。是從Activity的setContentView()開始的:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
實(shí)際是調(diào)用了Window的setContentView(),他分為下面的步驟:
- 如果沒有DecorView就創(chuàng)建它述暂,其中根據(jù)Theme的不同加載不同的布局格式痹升。
- 將Activity的布局的View添加到DecorView的mContentParent中,這里mContentParent指的就是@android:id/content所對(duì)應(yīng)的FrameLayout贸典,如下圖视卢,也就是圖中ContentViews那部分。
3.回調(diào)Activity的OnContentChanged()方法。
onResume()
真正想讓W(xué)indow中的視圖被看到妒挎,是在Activity的onResume()方法被調(diào)用之后才實(shí)現(xiàn)的绳锅,此時(shí)會(huì)調(diào)用WindowManager的addView()方法添加DecorView在Window中,再調(diào)用Activity的makeVisiable()方法使得View可見酝掩。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());//將DecorView添加到WindowManager
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);//DecorView可見
}
注:ViewRootImpl作為一個(gè)連接器鳞芙,當(dāng)用戶點(diǎn)擊屏幕時(shí),觸摸行為通過硬件傳遞來捕獲,然后交給ViewRootImpl原朝,接著將事件傳遞給DecorView驯嘱,再交給PhoneWindow,PhoneWindow再交給Activity喳坠,然后View事件分發(fā)鞠评。
2. Dialog的Window創(chuàng)建
和Activity類似,也是需要?jiǎng)?chuàng)建DecorView壕鹉,特殊之處在于需要采用Activity的Context剃幌,因?yàn)镈ialog創(chuàng)建需要應(yīng)用token,這個(gè)token只有Activity有晾浴,所以不能用Application的token负乡。一種解決的方法是將Window設(shè)置為系統(tǒng)層級(jí),就不需要token了脊凰。
3. Toast的Window創(chuàng)建
Toast屬于系統(tǒng)Window抖棘,比較復(fù)雜。
首先是Toast通過IPC遠(yuǎn)程調(diào)用NotificationManagerService的方法將請(qǐng)求添加到ToastQueue隊(duì)列中(最多容納50個(gè)Toast)笙各,然后NotificationManagerService再通過IPC遠(yuǎn)程調(diào)用Toast的TN對(duì)象的Binder钉答,實(shí)現(xiàn)視圖在Window中的添加和刪除。