Window是抽象類更卒,具體實(shí)現(xiàn)是PhoneWindow,通過WindowManager就可以創(chuàng)建Window。WindowManager是外界訪問Window的入口树肃,但是Window的具體實(shí)現(xiàn)是在WindowManagerService中狡汉,WindowManager和WindowManagerService的交互是一個(gè)IPC過程娄徊。所有的視圖例如Activity、Dialog盾戴、Toast都是附加在Window上的寄锐。
WindowManager添加View(應(yīng)用)
通過WindowManager添加View的過程:將一個(gè)Button添加到屏幕坐標(biāo)為(100,300)的位置上
private void initView() {
mCreateWindowButton = (Button) findViewById(R.id.button1);
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
}
public void onButtonClick(View v) {
if (v == mCreateWindowButton) {
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);
}
}
@Override
protected void onDestroy() {
try {
mWindowManager.removeView(mFloatingButton);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
super.onDestroy();
}
其中type如果設(shè)置為系統(tǒng) Window尖啡,必須要添加權(quán)限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
不然會報(bào)權(quán)限不足Error橄仆。
flags
參數(shù)解析:
FLAG_NOT_FOCUSABLE:表示window不需要獲取焦點(diǎn),也不需要接收各種輸入事件衅斩。此標(biāo)記會同時(shí)啟用FLAG_NOT_TOUCH_MODAL盆顾,最終事件會直接傳遞給下層的具有焦點(diǎn)的window;
FLAG_NOT_TOUCH_MODAL:在此模式下畏梆,系統(tǒng)會將window區(qū)域外的單擊事件傳遞給底層的window您宪,當(dāng)前window區(qū)域內(nèi)的單擊事件則自己處理,一般都需要開啟這個(gè)標(biāo)記奠涌;
FLAG_SHOW_WHEN_LOCKED:開啟此模式可以讓W(xué)indow顯示在鎖屏的界面上宪巨。
type
參數(shù)解析:
type參數(shù)表示window的類型,window共有三種類型:應(yīng)用window溜畅,子window和系統(tǒng)window捏卓。
window是分層的,每個(gè)window都對應(yīng)著z-ordered达皿,層級大的會覆蓋在層級小的上面天吓,應(yīng)用window的層級范圍是199,子window的層級范圍是10001999峦椰,系統(tǒng)window的層級范圍是2000~2999龄寞。
這些層級范圍對應(yīng)著 WindowManager.LayoutParams 的 type 參數(shù),如果想要 Window 位于所有 Window 的最頂層汤功,那么采用較大的層級即可物邑,很顯然系統(tǒng) Window 的層級是最大的,當(dāng)我們采用系統(tǒng)層級時(shí),需要聲明權(quán)限色解。
type的類型有很多茂嗓,詳見:http://blog.csdn.net/panda_xiao/article/details/51657555
demo運(yùn)行效果如下:
特別注意:該demo經(jīng)本人測試,兼容性較差
該demo在4.4(api19)的虛擬機(jī)不能達(dá)到系統(tǒng)Window效果(原因有待尋找)科阎,筆者虛擬機(jī)為5.1(api22)版本述吸,能達(dá)到預(yù)期效果。
還有一點(diǎn)要注意,api>=23之后type要是系統(tǒng)窗口(也就是type >= 2000)锣笨,則需要還一些權(quán)限才能使用,至于是什么權(quán)限我查了很多網(wǎng)頁都沒查出來,恐怕是ROOT權(quán)限吧
Window的內(nèi)部機(jī)制
Window是一個(gè)抽象的概念蝌矛,不是實(shí)際存在的,它也是以View的形式存在错英。在實(shí)際使用中無法直接訪問Window入撒,只能通過WindowManager才能訪問Window。每個(gè)Window都對應(yīng)著一個(gè)View和一個(gè)ViewRootImpl椭岩,Window和View通過ViewRootImpl來建立聯(lián)系茅逮。
-
Window的添加、刪除和更新過程都是IPC過程判哥,以Window的添加為例献雅,WindowManager的實(shí)現(xiàn)類對于addView、updateView和removeView方法都是委托給WindowManagerGlobal類塌计,該類保存了很多數(shù)據(jù)列表惩琉,例如所有window對應(yīng)的view集合mViews、所有window對應(yīng)的ViewRootImpl的集合mRoots夺荒、所有Window 所對應(yīng)的布局參數(shù)的WindowManager.LayoutParams集合mParams、正在被刪除的 View 對象的View集合mDyingViews良蒸。之后添加操作交給了ViewRootImpl來處理技扼,接著會通過WindowSession來完成Window的添加過程,這個(gè)過程是一個(gè)IPC調(diào)用嫩痰,因?yàn)樽罱K是通過WindowManagerService來完成window的添加的剿吻。
Window的創(chuàng)建過程
Activity的window創(chuàng)建過程
- Activity的啟動過程很復(fù)雜,最終會由ActivityThread中的performLaunchActivity來完成整個(gè)啟動過程串纺,在這個(gè)方法內(nèi)部會通過類加載器創(chuàng)建Activity的實(shí)例對象丽旅,并調(diào)用它的attach方法為其關(guān)聯(lián)運(yùn)行過程中所依賴的一系列上下文環(huán)境變量;
- Activity實(shí)現(xiàn)了Window的Callback接口纺棺,當(dāng)window接收到外界的狀態(tài)變化時(shí)就會回調(diào)Activity的方法榄笙,例如onAttachedToWindow、onDetachedFromWindow祷蝌、dispatchTouchEvent等茅撞;
- Activity的Window是由PolicyManager來創(chuàng)建的,它的真正實(shí)現(xiàn)是Policy類,它會新建一個(gè)PhoneWindow對象米丘,Activity的setContentView的實(shí)現(xiàn)是由PhoneWindow來實(shí)現(xiàn)的剑令;
- Activity的頂級View是DecorView,它本質(zhì)上是一個(gè)FrameLayout拄查。如果沒有DecorView吁津,那么PhoneWindow會先創(chuàng)建一個(gè)DecorView,然后加載具體的布局文件并將view添加到DecorView的mContentParent中堕扶,最后就是回調(diào)Activity的onContentChanged通知Activity視圖已經(jīng)發(fā)生了變化碍脏;
- 還有一個(gè)步驟是讓W(xué)indowManager能夠識別DecorView,在ActivityThread調(diào)用handleResumeActivity方法時(shí)挣柬,首先會調(diào)用Activity的onResume方法潮酒,然后會調(diào)用makeVisible方法,這個(gè)方法中DecorView真正地完成了添加和顯示過程邪蛔。
ViewManager vm = getWindowManager();
vm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
Dialog的Window創(chuàng)建過程
過程與Activity的Window創(chuàng)建過程類似急黎,普通的Dialog的有一個(gè)特別之處,即它必須采用Activity的Context侧到,如果采用Application的Context會報(bào)錯(cuò)勃教。原因是Application沒有應(yīng)用token,應(yīng)用token一般是Activity擁有的匠抗。
Toast的Window創(chuàng)建過程
- Toast屬于系統(tǒng)Window故源,它內(nèi)部的視圖由兩種方式指定:一種是系統(tǒng)默認(rèn)的演示;另一種是通過setView方法來指定一個(gè)自定義的View汞贸。
- Toast具有定時(shí)取消功能绳军,所以系統(tǒng)采用了Handler。Toast的顯示和隱藏是IPC過程矢腻,都需要NotificationManagerService來實(shí)現(xiàn)门驾。在Toast和NMS進(jìn)行IPC過程時(shí),NMS會跨進(jìn)程回調(diào)Toast中的TN類中的方法多柑,TN類是一個(gè)Binder類奶是,運(yùn)行在Binder線程池中,所以需要通過Handler將其切換到當(dāng)前發(fā)送Toast請求所在的線程竣灌,所以Toast無法在沒有Looper的線程中彈出聂沙。
- 對于非系統(tǒng)應(yīng)用來說,mToastQueue最多能同時(shí)存在50個(gè)ToastRecord初嘹,這樣做是為了防止DOS(Denial of Service及汉,拒絕服務(wù))。因?yàn)槿绻硞€(gè)應(yīng)用彈出太多的Toast會導(dǎo)致其他應(yīng)用沒有機(jī)會彈出Toast削樊。
總結(jié)
任何 View 都是附屬在一個(gè) Window 上面的豁生,Window 表示一個(gè)窗口的概念兔毒,也是一個(gè)抽象的概念,Window 并不是實(shí)際存在的甸箱,它是以 View 的形式存在的育叁。WindowManager 是外界也就是我們訪問 Window 的入口,Window 的具體實(shí)現(xiàn)位于 WindowManagerService 中芍殖,WindowManagerService 和 WindowManager 的交互是一個(gè) IPC 過程豪嗽。