理解Window和WindowManager

cryAllen發表於2017-02-04

Window表示一個視窗的概念,Window是一個抽象類,它的具體實現是PhoneWindow。建立一個Window,需要通過WindowManager即可完成,WindowManager是外界訪問Window的入口,Window具體實現位於WindowManagerService中,WindowManager和WindowManagerService的互動是一個IPC的過程。Android中,所有的檢視都是通過Window來呈現,不管是Activity、Dialog、還是Toast,它們的檢視實際上都是附加在Window上,因此Window是實際View的直接管理者,單擊事件由Window傳遞給DecorView,然後再由DecorView傳遞給我們的View,就連Activity的設定檢視方法setContentView在底層也是通過Window來完成的。

Window和WindowManager

新增一個Window的過程,重點程式碼是:

mWindowManager.addView(mFLoatingButton,mLayoutParams);

WindowManager.LayoutParams中有兩個flags和type引數。

Flags引數有三個Window屬性

  • FLAG_NOT_FOCUSABLE。表示Window不需要獲取焦點,也不需要接收各種輸入事件,最終事件會直接傳遞給下層的具有焦點的Window
  • FLAG_NOT_TOUCH_MODAL。在此模式下,系統會將當前Window區域以外的單擊事件傳遞給底層的Window,當前Window區域以內的單擊事件則自己處理,這個標記很重要,一般來說都需要開啟此標記,否則其他Window將無法收到單擊事件
  • FLAG_SHOW_WHEN_LOCKED。開啟此模式可以讓Window顯示在鎖屏的介面上。

Type參數列示Window的型別,有三種型別,分別是應用Window,子Window和系統Window,應用類Window對應一個Activity,子Window不能單獨存在,它需要附屬在特定的父Window之中,比如常見的Dialog就是一個子Window,系統Window是需要宣告許可權在能建立的Window,比如Toast和系統狀態列這些都是系統Window。

Window是分層的,每個Window都有對應的z-ordered,層級大的會覆蓋在層級小的Window的上面,在三類Window中,應用類的Window的層級範圍是1-99,子Window的層級範圍是1000-1999,系統Window的層級的範圍是2000-2999,這些層級範圍對應著WindowManager.LayoutParams的Type引數。如想要Window位於所有Window的最頂層,那麼採用較大的層級即可。很顯然系統Window層級是最大的,而且系統層級有很多值。

WindowManager所提供的功能很簡單,常用有三個方法,即新增View,更新View和刪除View,這三個方法定義在ViewManager中,而WindowManager繼承了ViewManager。

Window的內部機制

Window是一個抽象的概念,每一個Window都對應著一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯絡,說明View才是Window存在的實體,在實際使用中無法直接訪問Window,對Window的訪問必須通過WindowManager。

Window的新增過程

Window的新增過程需要通過WindowManager的addView來實現,WindowManager是一個介面,它的真正實現是WindowManagerImpl類。

@Override
public void addView(View view,ViewGroup.LayoutParams params){
  mGlobal.addView(view,params,mDisplay,mParentWindow);
}

@Override
public void updateViewLayout(View view,ViewGroup.LayoutParams params){
  mGlobal.updateViewLayout(view,params);
}

@Override
public void removeView(View view){
  mGlobal.removeView(view,false);
}

可以看到,WindowManagerImpl並沒有直接實現Window的三大操作,而是全部交給了WindowManagerGlobal來處理,WindowManagerGlobal以工廠的形式向外提供自己的例項。WindowManagerGlobal的addView方法主要分為如下幾步:

  • 檢查引數是否合法,如果是子Window那麼還需要調整一些佈局引數
  • 建立ViewRootImpl並將View新增到列表中
  • 通過ViewRootImpl來更新介面並完成Window的新增過程

Window的刪除過程

Window的刪除過程和新增過程一樣,都是先通過WindowManagerImpl後,在進一步通WindowManagerGlobal來實現的。裡面用到一個dispatchDetachedFromWindow方法內部實現,這個方法主要做了四件事:

  • 垃圾回收相關的工作,比如清除資料和訊息、移除回撥
  • 通過Session的remove方法刪除Window
  • 呼叫View的dispatchDetachedFromWindow方法,在內部會呼叫View的onDetachedFromWindow()以及onDetachedFromWindowInternal()
  • 呼叫WindowManagerGlobal的doRemoveView方法重新整理資料

Window的更新過程

主要是用到updateViewLayout方法,首先它需要更新View的LayoutParams並替換掉老的LayoutParams,接著再更新ViewRootImpl中的LayoutParams,這一步是通過ViewRootImpl的setLayoutParams方法來實現的。在ViewRootImpl中會通過scheduleTraversals方法對View進行重新佈局,包括測量、佈局、重繪這三個過程。

Window的建立過程

View是Android中的檢視呈現方式,但是View不能單獨存在,它必須附著在Window這個抽象的概念上面,因此有檢視的地方就有Window。

Activity的Window建立過程

如何建立,需要了解Activity啟動過程,比較複雜,但它最終由ActivityThred中的perfromLaunchActivity()來完成整個啟動過程,在這個方法內部會通過類載入器建立Activity的例項物件,並呼叫其attach方法為其關聯執行過程中所依賴的一系列上下文環境變數。

在Activity的attach方法裡,系統會建立Activity所屬的Window物件併為其設定回撥介面,Window物件的建立是通過PolicyManager的makeNewWindow方法實現的,對於Activity的setContentView的實現可以看出,Activity將具體實現交給了Window處理,而Window的具體實現是PhoneWindow,所以只需要看PhoneWindow相關邏輯即可,大致以下幾個步驟:

  • 如果沒有DecorView,那麼就建立它。DecorView是一個FrameLayout,是Activity的頂級View,一般來說它的內部包含標題欄和內部欄。
  • 將View新增到DecorView的mContentParent中。
  • 回撥Activity的onContentChanged方法通知Activity檢視已經發生改變。Activity的onContentChanged是一個空實現。

經過上面三個步驟,DecorView已經被建立初始化完畢,Activity的佈局檔案已經成功新增到了DecorView的mContentParent中,但是這個時候DecorView還沒有被WindowManager正式新增到Window中,真正被檢視呼叫是在Activity的onResume方法,接著會呼叫Activity的makeVisible(),正是在makeVisible方法中,DecorView真正地完成了新增和顯示這兩個過程。

Dialog的Window建立過程

Dialog的Window的建立過程和Activity類似,有以下幾個步驟:

  • 建立Window。同樣是通過PolicyManager的makeNewWindow方法來完成的。
  • 初始化DecorView並將Dialog的檢視新增到DecorView中。
  • 將DecorView新增到Window中並顯示。在Dialog的show方法中,會通過WindowManager將DecorView新增到Window中。

普通的Dialog有一個特殊之處,那就是必須採用Activity的Context,如果採用Application的Context,就會報錯。

Toast的Window建立過程

Toast和Dialog不同,它的工作過程稍微複雜。首先Toast也是基於Window來實現的,但是由於Toast具有定時取消這一功能,所以系統採用了Handler。在Toast的內部有兩類的IPC過程,第一類是Toast訪問NotificationManagerService,第二類是NotificationManagerService回撥Toast的TN介面。

Toast屬於系統Window,它內部的檢視有兩種方式指定,一種是系統預設的樣式,另一種是通過setView方法來指定一個自定義View,不管如何,它們都對應Toast的一個View型別的內部成員mNextView。Toast提供了show和cancel分別用於顯示和隱藏Toast,它們的內部是一個IPC過程。

閱讀擴充套件

源於對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。
1,Android系統簡介
2,ProGuard程式碼混淆
3,講講Handler+Looper+MessageQueue關係
4,Android圖片載入庫理解
5,談談Android執行時許可權理解
6,EventBus初理解
7,Android 常見工具類
8,對於Fragment的一些理解
9,Android 四大元件之 " Activity "
10,Android 四大元件之" Service "
11,Android 四大元件之“ BroadcastReceiver "
12,Android 四大元件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的理解
15,Android 生命週期和啟動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工作原理
19,理解 Window 和 WindowManager
20,Activity 啟動過程分析
21,Service 啟動過程分析
22,Android 效能優化
23,Android 訊息機制
24,Android Bitmap相關
25,Android 執行緒和執行緒池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸控事件機制
29,Android 事件機制應用
30,Cordova 框架的一些理解
31,有關 Android 外掛化思考
32,開發人員必備技能——單元測試

相關文章