第8章 理解Window和WindowManager

概述

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

8.1 Window和WindowManager

  1. 通過WindowManager添加Window的過程
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不需要獲取焦點,也不需要接收各種輸入事件。
    2. FLAG_NOT_TOUCH_MODAL:系統(tǒng)會將window區(qū)域外的單擊事件傳遞給底層的window,一般都需要開啟這個標記;
    3. FLAG_SHOW_WHEN_LOCKED:開啟此模式可以讓Window顯示在鎖屏的界面上。
  2. type參數(shù)表示window的類型,window共有三種類型:

    1. 應用window。應用window對應著一個Activity,層級范圍是1~99
    2. 子window。子window不能獨立存在,需要附屬在特定的父window之上,比如Dialog就是子window。層級范圍是1000~1999.
    3. 系統(tǒng)window。系統(tǒng)window是需要聲明權限才能創(chuàng)建的window,比如Toast和系統(tǒng)狀態(tài)欄這些都是系統(tǒng)window,需要聲明的權限是<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />。層級范圍是2000~2999
  3. WindowManager繼承自ViewManager,常用的只有三個方法:

    1. addView
    2. updateViewLayout
    3. removeView

8.2 Window的內部機制

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

8.2.1 Window的添加過程

  1. 檢查參數(shù)是否合法
  2. 創(chuàng)建ViewRootImpl將View添加到列表中
  3. 通過ViewRootImple更新界面并完成Window的添加過程。
    1. 在這里的setViez中會通過requesetLayout完成異步刷新請求。
    2. 會通過WindowSession對WMS進行Binder調用,由WMS實現(xiàn)Window的添加。WMS會對每個應用保留一個單獨的Session.

8.2.2 Window的刪除過程

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

8.2.3 Window的更新過程

  1. 更新View的LayoutParams
  2. 更新ViewRootImpl的LayoutParams
  3. 對View重新布局,包括measure、layout、draw三個過程

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

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

  1. Activity的啟動過程最終會由ActivityThread中的performLaunchActivity來完成整個啟動過程,在這個方法內部會通過類加載器創(chuàng)建Activity的實例對象,并調用它的attach方法為其關聯(lián)運行過程中所依賴的一系列上下文環(huán)境變量;
  2. Activity實現(xiàn)了Window的Callback接口,當window接收到外界的狀態(tài)變化時就會回調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,它本質上是一個FrameLayout。如果沒有DecorView,那么PhoneWindow會先創(chuàng)建一個DecorView,然后加載具體的布局文件并將view添加到DecorView的mContentParent中,最后就是回調Activity的onContentChanged通知Activity視圖已經發(fā)生了變化;
  5. 讓WindowManager能夠識別DecorView,在ActivityThread調用handleResumeActivity方法時,首先會調用Activity的onResume方法,然后會調用makeVisible方法,這個方法中DecorView真正地完成了添加和顯示過程。

8.3.2 Dialog的Window創(chuàng)建過程

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

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

  1. Toast屬于系統(tǒng)Window,它內部的視圖由兩種方式指定:一種是系統(tǒng)默認的演示;另一種是通過setView方法來指定一個自定義的View。
  2. Toast的顯示和隱藏是IPC過程,都需要NotificationManagerService來實現(xiàn)。在Toast和NMS進行IPC過程時,NMS會跨進程回調Toast中的TN類中的方法,TN類是一個Binder類,運行在Binder線程池中,所以需要通過Handler將其切換到當前發(fā)送Toast請求所在的線程,所以Toast無法在沒有Looper的線程中彈出。
  3. 對于非系統(tǒng)應用來說,mToastQueue最多能同時存在50個ToastRecord,這樣做是為了防止DOS(Denial of Service,拒絕服務)。因為如果某個應用彈出太多的Toast會導致其他應用沒有機會彈出Toast。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容