第8章 理解Window和WindowManager

概述

  1. Window是一個(gè)抽象類(lèi),具體實(shí)現(xiàn)是PhoneWindow。
  2. 創(chuàng)建一個(gè)Window,通過(guò)WindowManger就可以完成。WindowMangager是外界訪問(wèn)Window的入口
  3. Window的具體實(shí)現(xiàn)位于WindowMangerService(WMS),WindowManager和WMS的交互是一個(gè)IPC過(guò)程。
  4. Activity/Dialog/Toast,他們的視圖都是附加在Window上的。Window是View的直接管理者。

8.1 Window和WindowManager

  1. 通過(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);
  1. flags參數(shù)解析:

    1. FLAG_NOT_FOCUSABLE:表示window不需要獲取焦點(diǎn),也不需要接收各種輸入事件。
    2. FLAG_NOT_TOUCH_MODAL:系統(tǒng)會(huì)將window區(qū)域外的單擊事件傳遞給底層的window,一般都需要開(kāi)啟這個(gè)標(biāo)記;
    3. FLAG_SHOW_WHEN_LOCKED:開(kāi)啟此模式可以讓W(xué)indow顯示在鎖屏的界面上。
  2. type參數(shù)表示window的類(lèi)型,window共有三種類(lèi)型:

    1. 應(yīng)用window。應(yīng)用window對(duì)應(yīng)著一個(gè)Activity,層級(jí)范圍是1~99
    2. 子window。子window不能獨(dú)立存在,需要附屬在特定的父window之上,比如Dialog就是子window。層級(jí)范圍是1000~1999.
    3. 系統(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
  3. WindowManager繼承自ViewManager,常用的只有三個(gè)方法:

    1. addView
    2. updateViewLayout
    3. removeView

8.2 Window的內(nèi)部機(jī)制

  1. Window是一個(gè)抽象的概念,每個(gè)Window都對(duì)應(yīng)一個(gè)View和ViewRootImpl,Window和View通過(guò)ViewRootImpl聯(lián)系,Window是以View的形式存在。
  2. WindowManger是一個(gè)接口,真正實(shí)現(xiàn)是WindowManagerImpl類(lèi),WindowManagerImpl交給WindowMangerGlobal。WindowMangerGlobal以工廠的形式提供自己的實(shí)例。
  3. 此處是橋接模式,WindowManagerImple將所有的操作全部委托給WindowManagerGlobal實(shí)現(xiàn)。

8.2.1 Window的添加過(guò)程

  1. 檢查參數(shù)是否合法
  2. 創(chuàng)建ViewRootImpl將View添加到列表中
  3. 通過(guò)ViewRootImple更新界面并完成Window的添加過(guò)程。
    1. 在這里的setViez中會(huì)通過(guò)requesetLayout完成異步刷新請(qǐng)求。
    2. 會(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ò)程

  1. 通過(guò)findViewLocked查找待刪除的View的索引
  2. 調(diào)用removeViewLocked刪除
  3. 真正刪除View的邏輯在dispatchDetachedFromWindow
    1. 垃圾回收
    2. 通過(guò)Session的remove刪除Window,也是一個(gè)IPC過(guò)程
    3. onDetachedFromWindow(內(nèi)部資源回收,比如停止線程、終止動(dòng)畫(huà))
    4. 刷新mViews、mRoots、mDyingViews等數(shù)據(jù)

8.2.3 Window的更新過(guò)程

  1. 更新View的LayoutParams
  2. 更新ViewRootImpl的LayoutParams
  3. 對(duì)View重新布局,包括measure、layout、draw三個(gè)過(guò)程

8.3 Window的創(chuàng)建過(guò)程

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

  1. 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)境變量;
  2. Activity實(shí)現(xiàn)了Window的Callback接口,當(dāng)window接收到外界的狀態(tài)變化時(shí)就會(huì)回調(diào)Activity的方法,例如onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent等;
  3. 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)的;
  4. 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ā)生了變化;
  5. 讓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ò)程

  1. 創(chuàng)建Window 。通過(guò)PolicyManager的makeNewWindow完成
  2. setContentView,初始化DecorView,并將Dialog視圖放到DecorView中
  3. 將DecorView放到Window中顯示
  4. 當(dāng)Dialog關(guān)閉,通過(guò)WindowManger移除DecorView
  5. 普通Dialog必須才有Activity的Context,應(yīng)用Token只有Activity擁有。系統(tǒng)window不需要應(yīng)用Token

8.3.3 Toast的Window創(chuàng)建過(guò)程

  1. Toast屬于系統(tǒng)Window,它內(nèi)部的視圖由兩種方式指定:一種是系統(tǒng)默認(rèn)的演示;另一種是通過(guò)setView方法來(lái)指定一個(gè)自定義的View。
  2. 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的線程中彈出。
  3. 對(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。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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