(1)Window
是抽象類苛吱,具體實現(xiàn)是PhoneWindow
酪术,通過WindowManager
就可以創(chuàng)建Window。WindowManager是外界訪問Window的入口翠储,但是Window的具體實現(xiàn)是在WindowManagerService
中绘雁,WindowManager和WindowManagerService的交互是一個IPC過程橡疼。所有的視圖例如Activity、Dialog咧七、Toast都是附加在Window上的衰齐。
(2)通過WindowManager添加View的過程:將一個Button添加到屏幕坐標(biāo)為(100,300)的位置上
mFloatingButton = new Button(this);
mFloatingButton.setText("test button");
mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
PixelFormat.TRANSPARENT);//0,0 分別是type和flags參數(shù),在后面分別配置了
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不需要獲取焦點继阻,也不需要接收各種輸入事件耻涛。此標(biāo)記會同時啟用FLAG_NOT_TOUCH_MODAL
,最終事件會直接傳遞給下層的具有焦點的window瘟檩;
FLAG_NOT_TOUCH_MODAL
:在此模式下抹缕,系統(tǒng)會將window區(qū)域外的單擊事件傳遞給底層的window,當(dāng)前window區(qū)域內(nèi)的單擊事件則自己處理墨辛,一般都需要開啟這個標(biāo)記卓研;
FLAG_SHOW_WHEN_LOCKED
:開啟此模式可以讓W(xué)indow顯示在鎖屏的界面上。 [奇怪的是我刪除這個標(biāo)記還是在鎖屏看到了添加的組件orz]
type參數(shù)表示window的類型睹簇,window共有三種類型:應(yīng)用window奏赘,子window和系統(tǒng)window。應(yīng)用window對應(yīng)著一個Activity太惠,子window不能獨立存在磨淌,需要附屬在特定的父window之上,比如Dialog就是子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" />
埃脏。
(3)window是分層的搪锣,每個window都對應(yīng)著z-ordered
,層級大的會覆蓋在層級小的上面彩掐,應(yīng)用window的層級范圍是1~99
构舟,子window的層級范圍是1000~1999
,系統(tǒng)window的層級范圍是2000~2999
堵幽。
[注意旁壮,應(yīng)用window的層級范圍并不是1~999
喲]
(4)WindowManager繼承自ViewManager
,常用的只有三個方法:addView
谐檀、updateView
和removeView
抡谐。
8.2 Window的內(nèi)部機(jī)制
(1)Window是一個抽象的概念,不是實際存在的桐猬,它也是以View的形式存在麦撵。在實際使用中無法直接訪問Window,只能通過WindowManager才能訪問Window。每個Window都對應(yīng)著一個View和一個ViewRootImpl
免胃,Window和View通過ViewRootImpl來建立聯(lián)系音五。
(2)Window的添加、刪除和更新過程都是IPC過程羔沙,以Window的添加為例躺涝,WindowManager的實現(xiàn)類對于addView
、updateView
和removeView
方法都是委托給WindowManagerGlobal
類扼雏,該類保存了很多數(shù)據(jù)列表坚嗜,例如所有window對應(yīng)的view集合mViews
、所有window對應(yīng)的ViewRootImpl的集合mRoots
等诗充,之后添加操作交給了ViewRootImpl來處理苍蔬,接著會通過WindowSession
來完成Window的添加過程,這個過程是一個IPC調(diào)用蝴蜓,因為最終是通過WindowManagerService
來完成window的添加的碟绑。
8.3 Window的創(chuàng)建過程
(1)Activity的window創(chuàng)建過程
1.Activity的啟動過程很復(fù)雜,最終會由ActivityThread
中的performLaunchActivity
來完成整個啟動過程茎匠,在這個方法內(nèi)部會通過類加載器創(chuàng)建Activity的實例對象格仲,并調(diào)用它的attach
方法為其關(guān)聯(lián)運行過程中所依賴的一系列上下文環(huán)境變量;
2.Activity實現(xiàn)了Window的Callback
接口诵冒,當(dāng)window接收到外界的狀態(tài)變化時就會回調(diào)Activity的方法凯肋,例如onAttachedToWindow
、onDetachedFromWindow
造烁、dispatchTouchEvent
等否过;
3.Activity的Window是由PolicyManager
來創(chuàng)建的午笛,它的真正實現(xiàn)是Policy
類惭蟋,它會新建一個PhoneWindow
對象,Activity的setContentView
的實現(xiàn)是由PhoneWindow
來實現(xiàn)的药磺;
4.Activity的頂級View是DecorView
告组,它本質(zhì)上是一個FrameLayout
。如果沒有DecorView癌佩,那么PhoneWindow會先創(chuàng)建一個DecorView木缝,然后加載具體的布局文件并將view添加到DecorView的mContentParent
中,最后就是回調(diào)Activity的onContentChanged
通知Activity視圖已經(jīng)發(fā)生了變化围辙;
5.還有一個步驟是讓W(xué)indowManager能夠識別DecorView我碟,在ActivityThread
調(diào)用handleResumeActivity
方法時,首先會調(diào)用Activity的onResume方法姚建,然后會調(diào)用makeVisible
方法矫俺,這個方法中DecorView真正地完成了添加和顯示過程。
ViewManager vm = getWindowManager();
vm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
(2)Dialog的Window創(chuàng)建過程
1.過程與Activity的Window創(chuàng)建過程類似,普通的Dialog的有一個特別之處厘托,即它必須采用Activity的Context友雳,如果采用Application的Context會報錯。原因是Application沒有應(yīng)用token
铅匹,應(yīng)用token一般是Activity擁有的押赊。[service貌似也有token?]
(3)Toast的Window創(chuàng)建過程
1.Toast屬于系統(tǒng)Window,它內(nèi)部的視圖由兩種方式指定:一種是系統(tǒng)默認(rèn)的演示包斑;另一種是通過setView
方法來指定一個自定義的View流礁。
2.Toast具有定時取消功能,所以系統(tǒng)采用了Handler
舰始。Toast的顯示和隱藏是IPC過程崇棠,都需要NotificationManagerService
來實現(xiàn)。在Toast和NMS進(jìn)行IPC過程時丸卷,NMS會跨進(jìn)程回調(diào)Toast中的TN
類中的方法枕稀,TN類是一個Binder類,運行在Binder線程池中谜嫉,所以需要通過Handler將其切換到當(dāng)前發(fā)送Toast請求所在的線程萎坷,所以Toast無法在沒有Looper的線程中彈出。
3.對于非系統(tǒng)應(yīng)用來說沐兰,mToastQueue
最多能同時存在50
個ToastRecord
哆档,這樣做是為了防止DOS
(Denial of Service,拒絕服務(wù))住闯。因為如果某個應(yīng)用彈出太多的Toast會導(dǎo)致其他應(yīng)用沒有機(jī)會彈出Toast瓜浸。
其他學(xué)習(xí)資料