Window和WindowManager小結(jié)

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)行效果如下:

5.1版本運(yùn)行效果.gif

特別注意:該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ī)制

  1. Window是一個(gè)抽象的概念蝌矛,不是實(shí)際存在的,它也是以View的形式存在错英。在實(shí)際使用中無法直接訪問Window入撒,只能通過WindowManager才能訪問Window。每個(gè)Window都對應(yīng)著一個(gè)View和一個(gè)ViewRootImpl椭岩,Window和View通過ViewRootImpl來建立聯(lián)系茅逮。

  2. 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的添加的剿吻。


    圖示總結(jié)(來源于網(wǎng)絡(luò))

Window的創(chuàng)建過程

Activity的window創(chuàng)建過程

  1. Activity的啟動過程很復(fù)雜,最終會由ActivityThread中的performLaunchActivity來完成整個(gè)啟動過程串纺,在這個(gè)方法內(nèi)部會通過類加載器創(chuàng)建Activity的實(shí)例對象丽旅,并調(diào)用它的attach方法為其關(guān)聯(lián)運(yùn)行過程中所依賴的一系列上下文環(huán)境變量;
  2. Activity實(shí)現(xiàn)了Window的Callback接口纺棺,當(dāng)window接收到外界的狀態(tài)變化時(shí)就會回調(diào)Activity的方法榄笙,例如onAttachedToWindow、onDetachedFromWindow祷蝌、dispatchTouchEvent等茅撞;
  3. Activity的Window是由PolicyManager來創(chuàng)建的,它的真正實(shí)現(xiàn)是Policy類,它會新建一個(gè)PhoneWindow對象米丘,Activity的setContentView的實(shí)現(xiàn)是由PhoneWindow來實(shí)現(xiàn)的剑令;
  4. Activity的頂級View是DecorView,它本質(zhì)上是一個(gè)FrameLayout拄查。如果沒有DecorView吁津,那么PhoneWindow會先創(chuàng)建一個(gè)DecorView,然后加載具體的布局文件并將view添加到DecorView的mContentParent中堕扶,最后就是回調(diào)Activity的onContentChanged通知Activity視圖已經(jīng)發(fā)生了變化碍脏;
  5. 還有一個(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)建過程

  1. Toast屬于系統(tǒng)Window故源,它內(nèi)部的視圖由兩種方式指定:一種是系統(tǒng)默認(rèn)的演示;另一種是通過setView方法來指定一個(gè)自定義的View汞贸。
  2. 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的線程中彈出聂沙。
  3. 對于非系統(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 過程豪嗽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市豌骏,隨后出現(xiàn)的幾起案子龟梦,更是在濱河造成了極大的恐慌,老刑警劉巖窃躲,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件计贰,死亡現(xiàn)場離奇詭異,居然都是意外死亡蒂窒,警方通過查閱死者的電腦和手機(jī)躁倒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洒琢,“玉大人秧秉,你說我怎么就攤上這事∷ヒ郑” “怎么了象迎?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呛踊。 經(jīng)常有香客問我砾淌,道長,這世上最難降的妖魔是什么谭网? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任拇舀,我火速辦了婚禮,結(jié)果婚禮上蜻底,老公的妹妹穿的比我還像新娘。我一直安慰自己聘鳞,他們只是感情好薄辅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抠璃,像睡著了一般站楚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搏嗡,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天窿春,我揣著相機(jī)與錄音拉一,去河邊找鬼。 笑死旧乞,一個(gè)胖子當(dāng)著我的面吹牛蔚润,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尺栖,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼嫡纠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了延赌?” 一聲冷哼從身側(cè)響起除盏,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挫以,沒想到半個(gè)月后者蠕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掐松,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年踱侣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甩栈。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泻仙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出量没,到底是詐尸還是另有隱情玉转,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布殴蹄,位于F島的核電站究抓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袭灯。R本人自食惡果不足惜刺下,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稽荧。 院中可真熱鬧橘茉,春花似錦、人聲如沸姨丈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蟋恬。三九已至翁潘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歼争,已是汗流浹背拜马。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工渗勘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俩莽。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓旺坠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親豹绪。 傳聞我的和親對象是個(gè)殘疾皇子价淌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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