第8章 理解Window和WindowManager
Window表示一個窗口的概念。Window是一個抽象類,它的具體實現是PhoneWindow。WindowManager是外界訪問Window的入口,Window的具體實現位於WindowManagerService中,WindowManager和WindowManagerService的交互是一個IPC過程。Android中所有的視圖都是通過Window來呈現的,他們的視圖實際上都是附加在Window上的,因此,Window實際是View的直接管理者。
8.1 Window和WindowManager
通過WindowManager添加Window的過程:
WindowManager windowManager=(WindowManager) getSystemService(Context.WINDOW_SERVICE);
Button button=new Button(this);
button.setText("this is a button");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("click");
Intent intent=new Intent(v.getContext(), RemoteActivity.class);
startActivity(intent);
}
});
WindowManager.LayoutParams layoutParams=new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSPARENT);
layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
layoutParams.gravity= Gravity.LEFT|Gravity.TOP;
layoutParams.x=100;
layoutParams.y=300;
windowManager.addView(button,layoutParams);
Flags參數表示Window的屬性有幾個常用的選項:
- FLAG_NOT_FOCUSABLE:表示Window不需要獲取焦點,也不需要接收各種輸入事件,此標記會同時啓用FLAG_NOT_TOUCH_MODAL,最終事件會直接傳遞個下層的具有焦點的Window。
- FLAG_NOT_TOUCH_MODAL:在此模式下,系統會將當前Window區域以外的單擊事件傳遞給底層的Window,當前Window區域以內的單擊事件則自己處理。
- FLAG_SHOW_WHEN_LOCKED:開啓此模式可以讓Window顯示在鎖屏的界面上。
Type參數表示Window的類型,Window有三種類型,分別是應用Window,子Window和系統Window。應用類Window對應着一個Activity。子Window不能單獨存在,它需要附屬在特定的父Window之中,比如Dialog。系統Window是需要聲明權限才能創建的Window,比如Toast和系統狀態欄。
Window是分層的,每個Window都有對應的z-ordered,層級大的會覆蓋在層級小的Window上面。應用Window的層級範圍是1~99,子Window的層級範圍是1000~1999,系統Window的層級範圍是2000~2999。這些層級範圍對應着WindowManager.LayoutParams的type參數。
8.2 Window的內部機制
8.2.1 Window的添加過程
Window的添加過程需要通過WindowManager的addView來實現。
- 檢查參數是否合法,如果是子Window那麼還需要調整一些佈局參數
- 創建ViewRootImp並將View添加到列表中
- 通過ViewRootImpl來更新界面並完成Window的添加過程:這個過程由ViewRootImp的setView方法來完成,在setView內部會通過requestLayout來完成異步刷新請求,接着通過WindowSession最終來完成Window的添加過程。
8.2.2 Window的刪除過程
WindowManager中提供了兩種刪除接口removeView和removeViewImmediate,他們分別表示異步刪除和同步刪除,其中remvoeViewImmediate使用起來需要特別注意,一般來說不需要使用此方法來刪除Window以免發生意外錯誤。
- removeView()
- removeViewLocked()
- die()
- 同步刪除doDie(),異步刪除發送MSG_DIE消息
- doDie()內部調用dispatchDetachedFromWindow方法
- dispatchDetachedFromWindow方法內部調用WindowSession.remove(mWindow);
8.2.3 Window的更新過程
- 調用WindowManager的updateViewLayout方法
- 調用WindowManagerGlobal的updateViewLayout方法
- 調用root.setLayoutParams()方法
- 調用scheduleTraversals()方法,開始重新測量,佈局,繪製
8.3 Window的創建過程
8.3.1 Activity的Window創建過程
Window創建流程:
PhoneWindow的setContentView方法步驟:
- 如果沒有DecorView,那麼就創建它
DercorView是一個FrameLayout,DecorView是Activity中的頂級View,一般來說它的內部包含標題欄和內部蘭,但是這個會隨着主題的變換而發生改變。內容欄是一定要存在的,並且內容欄具有固定的id,就是“content”,它的完整id是android.R.id.content。
將View添加到DecorView的mContentParent中
回調Activity的onContentChanged方法通知Activity視圖已經發生改變
Activity的佈局文件已經成功添加熬了DercorView的mContentParent中,但是這個時候DecorView還沒有被WindowManager正式添加到Window中。在ActivityThread的handleResumeActivity方法中,首先會調用Activity的onResume方法,接着會調用Activity的makeVisible()。
大致流程:
###8.3.2 Dialog的Window創建過程
Dialog的Window的創建過程和Activity類似。
- 創建Window
- 初始化DercorView並將Dialog的視圖添加到DercorView中
- 將DecorView添加到Window中並顯示
普通的Dialog有一個特殊之處,那就是必須採用Activity的Context,如果採用Application的Context,那麼就會報錯。
系統Window比較特殊,它可以不需要token,但是使用時需要在AndroidManifest文件中聲明權限。
8.3.3 Toast的Window創建過程
在Tost的內部有兩類IPC過程,第一類是Toast訪問NotificationManagerService,第二類是NotificationManagerService回調Toast裏的TN接口。
當NotificationManagerService處理Toast的顯示或隱藏請求時會跨進程回調TN中的方法,這個時候由於TN運行在Binder線程池中,所以需要通過Handler將其切換到當前線程中。這裏的線程是指發送Toast請求所在的線程。
流程: