第八章,理解Window及WindowManager
Window及WindowManager
Window內部機制
Window創建過程
Window
是一個抽象類,實現類是PhoneWindow
, 創建Window只需要通過WindowManager
Window
的具體實現在WindowManagerService
中,WindowManager
和WindowManagerService
是一個IPC
過程Window
是View
的直接管理者
Window及WindowManager
通過WindowManager.addView
方法即可將View添加到Window,
mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
PixelFormat.TRANSPARENT);
//Window的屬性
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL//一般需要開啓
| LayoutParams.FLAG_NOT_FOCUSABLE//Window不需要獲取焦點,不需要各種輸入事件,需要同時開啓FLAG_NOT_TOUCH_MODAL,使事件下傳.
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;//讓window顯示在鎖屏上
//window的類型,應用widow,子window,系統window
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;//顯示位置
mWindowManager.addView(mFloatingButton, mLayoutParams);
Window
層級,z-ordered
.層級大的會覆蓋在小的上面
.對應着mLayoutParams.type
參數
應用window: 1~99
子window: 1000~1999
系統window: 2000~2999
WindowManager
功能只有三個方法,即添加View,更新View,刪除View
,
WindowManager
繼承了ViewManager
,操作Window
的過程更像是操作Window
中的View.
Window內部機制
Window
是一個抽象概念,每個Window
對應着一個View和一個ViewRootImpl
ViewRootImpl
Window <-----------------> View
Window
並不是實際存在的,它是以View
的形式存在的.View
纔是Window
存在的實體
Window的添加過程
//橋接模式
WindowManagerImpl#addView --> WindowManagerGlobal#addView
WindowManagerGlobal
添加過程:
- 檢查參數是否合法
- 創建
ViewRoorImpl將View添加
到列表中. - 通過
ViewRoorImpl更新界面
(調用requestLayout
)
ViewRootImpl -> Session(WindowSession的實現類)
-> WindowManagerService#addWindow
Window的刪除過程
WindowManagerImpl#removeView -> WindowManagerGlobal#removeView
-> findViewLocked -> removeViewLocked
-> ViewRootImpl#die -> dispatchDetachedFromWindow
- 通過
findViewLocked
查找待刪除的View的索引. removeViewLocked
調用ViewRootImpl
的刪除方法來做進一步的刪除.removeView
和removeViewImmediate
,分別代表異步和同步刪除
ViewRootImpl#die
中做了判斷,如果是同步刪除,則調用doDie
如果是異步刪除,就發送一個MSG_DIE
,ViewRootImpl#Handler
會處理此消息並調用doDie
.
dispatchDetachedFromWindow
所做的工作
1. 垃圾回收相關的工作
2. 通過Session#remove
刪除Window
,IPC
操作WindowManagerService#removeWindow
3. 調用View#onDetachedFromWindow
,終止動畫,停止線程
4. 調用WindowManagerGlobal#doRemoveView
刷新數據.
Window的更新過程
依然是調用WindowManagerGlobal#updateViewLayout
.
- 更新View的
LayoutParams
,並替換掉老的LayoutParams
. - 通過
ViewRootImpl#setLayoutParams
更新ViewRootImpl的LayoutParams
ViewRootImpl#scheduleTraversals
重新測量,佈局,繪製
Window創建過程
view
不能單獨存在,必須附着在window
之上,有視圖的地方就有widow.
Android
中可提供視圖的地方有Activity
,Dialog
,Toast
,PopUpWindow
,菜單
.
Activity的Window創建過程
Activity
的啓動過程最終由ActivityThread#performLaunchActivity
完成.
方法內部會通過類加載器加載Activity
的實例.並調用attach
方法關聯運行過程中上下文環境.- 在
attach
方法中會調用PolicyManager#makeNewWindow
創建window對象,並設置回調接口. Activity
實現了Window.CallBack
,因此,當狀態改變時會回調Activity.
PolicyManager
是一個策略類,方法都在IPolicy
接口中聲明瞭.真正實現類是Policy
PolicyManager
與Policy
的關聯,可能是在編譯環節動態控制的
.- 通過
Activity#setContentView
將視圖附着到window上
- 如果沒有
DecorView
,就創建它- 將
View
添加到DecorView.mContentParent
中- 回調
Activity#onContentChanged
經過上面的步驟,DecorView
並沒有添加到Window
,Activity
還無法接受輸入事件,具體加入步驟如下:
Activity#attach -> PolicyManager#makeNewWindow
-> Activity#setContentView -> ActivityThread#handleResumeActivity -> Activity#onResume
-> Activity#makeVisible -> mWindowManager.addView//添加DecorView
Dialog的Window創建過程
- 創建
Window
,依然是PolicyManager#makeNewWindow
- 初始化
DecorView
,並將Dialog視圖添加到DecorView
- 將
DecorView
添加到Window
中並顯示 Dialog
關閉,則通過windowManager#removeViewImmediate(mDecor)
移除.
注意: 普通
Dialog
必須採用Activity
的Window
Toast的Window創建過程
Toast
的工作過程比Dialog
稍顯複雜,Toast
具有定時取消這一功能(採用Handler
).- 在
Toast
內部有兩類IPC過程
.一類,Toast->NotificationManagerService
,二類,NotificationManagerService -> Toast#TN
Toast
屬於系統Window
,內部視圖可以通過setView
來指定Toast
提供show和cancel
方法,他們是一個IPC過程
,都是通過NMS
完成TN是一個Binder類
,NMS
回調TN
來處理Toast的show和cancel
- 由於
TN
運行在Binder線程池
中,因此需要使用Handler(必須在有Looper的線程中彈出Toast)
LONG_DELAY = 3.5s,SHORT_DELAY = 2s
,延遲消息發送後就會通過cancelToastLocked
來隱藏Toast
,並從mToastQueue
中移除.
// 將Toast封裝爲ToastRecord添加到mToastQueue中
Toast#show -> NMS#enqueueToast -> mToastQueue#add(ToastRecord)
// mToastQueue對於非系統應用,最多同時50個ToastRecord,防止DOS
-> NMS#showNextToastLocked ->toastRecord.callBack#show
// 這裏callback就是Toast#TN對象的遠程Binder,最終回調TN中的方法運行在發起Toast的線程池中.
//顯示後,延時關閉
scheduleTimeoutLocked(ToastRecord)->sendMessageDelayed
-> toastRecord.callBack#hide -> mHandler.post(mShow)
-> TN#handleHide -> mWm.removeView
//這裏 callback 就是 Toast#TN 的遠程Binder,運行在Binder線程池中,需要用Handler切換線程,最終通過 TN#handleHide 調用 WindowManager 移除 View
擴展閱讀
淺析Android的窗口