概述
- Window是一個(gè)抽象類(lèi),具體實(shí)現(xiàn)是PhoneWindow。
- 創(chuàng)建一個(gè)Window,通過(guò)WindowManger就可以完成。WindowMangager是外界訪問(wèn)Window的入口
- Window的具體實(shí)現(xiàn)位于WindowMangerService(WMS),WindowManager和WMS的交互是一個(gè)IPC過(guò)程。
- Activity/Dialog/Toast,他們的視圖都是附加在Window上的。Window是View的直接管理者。
8.1 Window和WindowManager
- 通過(guò)WindowManager添加Window的過(guò)程
mFloatingButton = new Button(this);
mFloatingButton.setText("test button");
mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
PixelFormat.TRANSPARENT);
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mFloatingButton.setOnTouchListener(this);
mWindowManager.addView(mFloatingButton, mLayoutParams);
-
flags參數(shù)解析:
- FLAG_NOT_FOCUSABLE:表示window不需要獲取焦點(diǎn),也不需要接收各種輸入事件。
- FLAG_NOT_TOUCH_MODAL:系統(tǒng)會(huì)將window區(qū)域外的單擊事件傳遞給底層的window,一般都需要開(kāi)啟這個(gè)標(biāo)記;
- FLAG_SHOW_WHEN_LOCKED:開(kāi)啟此模式可以讓W(xué)indow顯示在鎖屏的界面上。
-
type參數(shù)表示window的類(lèi)型,window共有三種類(lèi)型:
- 應(yīng)用window。應(yīng)用window對(duì)應(yīng)著一個(gè)Activity,層級(jí)范圍是1~99
- 子window。子window不能獨(dú)立存在,需要附屬在特定的父window之上,比如Dialog就是子window。層級(jí)范圍是1000~1999.
- 系統(tǒng)window。系統(tǒng)window是需要聲明權(quán)限才能創(chuàng)建的window,比如Toast和系統(tǒng)狀態(tài)欄這些都是系統(tǒng)window,需要聲明的權(quán)限是<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />。層級(jí)范圍是2000~2999
-
WindowManager繼承自ViewManager,常用的只有三個(gè)方法:
- addView
- updateViewLayout
- removeView
8.2 Window的內(nèi)部機(jī)制
- Window是一個(gè)抽象的概念,每個(gè)Window都對(duì)應(yīng)一個(gè)View和ViewRootImpl,Window和View通過(guò)ViewRootImpl聯(lián)系,Window是以View的形式存在。
- WindowManger是一個(gè)接口,真正實(shí)現(xiàn)是WindowManagerImpl類(lèi),WindowManagerImpl交給WindowMangerGlobal。WindowMangerGlobal以工廠的形式提供自己的實(shí)例。
- 此處是橋接模式,WindowManagerImple將所有的操作全部委托給WindowManagerGlobal實(shí)現(xiàn)。
8.2.1 Window的添加過(guò)程
- 檢查參數(shù)是否合法
- 創(chuàng)建ViewRootImpl將View添加到列表中
- 通過(guò)ViewRootImple更新界面并完成Window的添加過(guò)程。
- 在這里的setViez中會(huì)通過(guò)requesetLayout完成異步刷新請(qǐng)求。
- 會(huì)通過(guò)WindowSession對(duì)WMS進(jìn)行Binder調(diào)用,由WMS實(shí)現(xiàn)Window的添加。WMS會(huì)對(duì)每個(gè)應(yīng)用保留一個(gè)單獨(dú)的Session.
8.2.2 Window的刪除過(guò)程
- 通過(guò)findViewLocked查找待刪除的View的索引
- 調(diào)用removeViewLocked刪除
- 真正刪除View的邏輯在dispatchDetachedFromWindow
- 垃圾回收
- 通過(guò)Session的remove刪除Window,也是一個(gè)IPC過(guò)程
- onDetachedFromWindow(內(nèi)部資源回收,比如停止線程、終止動(dòng)畫(huà))
- 刷新mViews、mRoots、mDyingViews等數(shù)據(jù)
8.2.3 Window的更新過(guò)程
- 更新View的LayoutParams
- 更新ViewRootImpl的LayoutParams
- 對(duì)View重新布局,包括measure、layout、draw三個(gè)過(guò)程
8.3 Window的創(chuàng)建過(guò)程
8.3.1 Activity的window創(chuàng)建過(guò)程
- Activity的啟動(dòng)過(guò)程最終會(huì)由ActivityThread中的performLaunchActivity來(lái)完成整個(gè)啟動(dòng)過(guò)程,在這個(gè)方法內(nèi)部會(huì)通過(guò)類(lèi)加載器創(chuàng)建Activity的實(shí)例對(duì)象,并調(diào)用它的attach方法為其關(guān)聯(lián)運(yùn)行過(guò)程中所依賴(lài)的一系列上下文環(huán)境變量;
- Activity實(shí)現(xiàn)了Window的Callback接口,當(dāng)window接收到外界的狀態(tài)變化時(shí)就會(huì)回調(diào)Activity的方法,例如onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent等;
- Activity的Window是由PolicyManager來(lái)創(chuàng)建的,它的真正實(shí)現(xiàn)是Policy類(lèi),它會(huì)新建一個(gè)PhoneWindow對(duì)象,Activity的setContentView的實(shí)現(xiàn)是由PhoneWindow來(lái)實(shí)現(xiàn)的;
- Activity的頂級(jí)View是DecorView,它本質(zhì)上是一個(gè)FrameLayout。如果沒(méi)有DecorView,那么PhoneWindow會(huì)先創(chuàng)建一個(gè)DecorView,然后加載具體的布局文件并將view添加到DecorView的mContentParent中,最后就是回調(diào)Activity的onContentChanged通知Activity視圖已經(jīng)發(fā)生了變化;
- 讓W(xué)indowManager能夠識(shí)別DecorView,在ActivityThread調(diào)用handleResumeActivity方法時(shí),首先會(huì)調(diào)用Activity的onResume方法,然后會(huì)調(diào)用makeVisible方法,這個(gè)方法中DecorView真正地完成了添加和顯示過(guò)程。
8.3.2 Dialog的Window創(chuàng)建過(guò)程
- 創(chuàng)建Window 。通過(guò)PolicyManager的makeNewWindow完成
- setContentView,初始化DecorView,并將Dialog視圖放到DecorView中
- 將DecorView放到Window中顯示
- 當(dāng)Dialog關(guān)閉,通過(guò)WindowManger移除DecorView
- 普通Dialog必須才有Activity的Context,應(yīng)用Token只有Activity擁有。系統(tǒng)window不需要應(yīng)用Token
8.3.3 Toast的Window創(chuàng)建過(guò)程
- Toast屬于系統(tǒng)Window,它內(nèi)部的視圖由兩種方式指定:一種是系統(tǒng)默認(rèn)的演示;另一種是通過(guò)setView方法來(lái)指定一個(gè)自定義的View。
- Toast的顯示和隱藏是IPC過(guò)程,都需要NotificationManagerService來(lái)實(shí)現(xiàn)。在Toast和NMS進(jìn)行IPC過(guò)程時(shí),NMS會(huì)跨進(jìn)程回調(diào)Toast中的TN類(lèi)中的方法,TN類(lèi)是一個(gè)Binder類(lèi),運(yùn)行在Binder線程池中,所以需要通過(guò)Handler將其切換到當(dāng)前發(fā)送Toast請(qǐng)求所在的線程,所以Toast無(wú)法在沒(méi)有Looper的線程中彈出。
- 對(duì)于非系統(tǒng)應(yīng)用來(lái)說(shuō),mToastQueue最多能同時(shí)存在50個(gè)ToastRecord,這樣做是為了防止DOS(Denial of Service,拒絕服務(wù))。因?yàn)槿绻硞€(gè)應(yīng)用彈出太多的Toast會(huì)導(dǎo)致其他應(yīng)用沒(méi)有機(jī)會(huì)彈出Toast。