開發藝術探索-- Window及WindowManager

第八章,理解Window及WindowManager

Window及WindowManager
Window內部機制
Window創建過程

  1. Window是一個抽象類,實現類是PhoneWindow, 創建Window只需要通過WindowManager
  2. Window的具體實現在WindowManagerService中,WindowManagerWindowManagerService是一個IPC過程
  3. WindowView的直接管理者

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添加過程:

  1. 檢查參數是否合法
  2. 創建ViewRoorImpl將View添加到列表中.
  3. 通過ViewRoorImpl更新界面(調用requestLayout)
ViewRootImpl -> Session(WindowSession的實現類) 
-> WindowManagerService#addWindow

Window的刪除過程

WindowManagerImpl#removeView -> WindowManagerGlobal#removeView 
-> findViewLocked -> removeViewLocked 
-> ViewRootImpl#die -> dispatchDetachedFromWindow
  1. 通過findViewLocked查找待刪除的View的索引.
  2. removeViewLocked調用ViewRootImpl的刪除方法來做進一步的刪除.
  3. removeViewremoveViewImmediate,分別代表異步和同步刪除

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.

  1. 更新View的LayoutParams,並替換掉老的LayoutParams.
  2. 通過ViewRootImpl#setLayoutParams更新ViewRootImpl的LayoutParams
  3. ViewRootImpl#scheduleTraversals重新測量,佈局,繪製

Window創建過程

  1. view不能單獨存在,必須附着在window之上,有視圖的地方就有widow.
  2. Android中可提供視圖的地方有Activity,Dialog,Toast,PopUpWindow,菜單.

Activity的Window創建過程

  1. Activity的啓動過程最終由ActivityThread#performLaunchActivity完成.
    方法內部會通過類加載器加載Activity的實例.並調用attach方法關聯運行過程中上下文環境.
  2. attach方法中會調用PolicyManager#makeNewWindow創建window對象,並設置回調接口.
  3. Activity實現了Window.CallBack,因此,當狀態改變時會回調Activity.
  4. PolicyManager是一個策略類,方法都在IPolicy接口中聲明瞭.真正實現類是Policy
  5. PolicyManagerPolicy的關聯,可能是在編譯環節動態控制的.
  6. 通過Activity#setContentView將視圖附着到window上
  1. 如果沒有DecorView,就創建它
  2. View添加到DecorView.mContentParent
  3. 回調Activity#onContentChanged

經過上面的步驟,DecorView並沒有添加到Window,Activity還無法接受輸入事件,具體加入步驟如下:

Activity#attach -> PolicyManager#makeNewWindow 
-> Activity#setContentView  -> ActivityThread#handleResumeActivity -> Activity#onResume 
-> Activity#makeVisible -> mWindowManager.addView//添加DecorView

Dialog的Window創建過程

  1. 創建Window,依然是PolicyManager#makeNewWindow
  2. 初始化DecorView,並將Dialog視圖添加到DecorView
  3. DecorView添加到Window中並顯示
  4. Dialog關閉,則通過windowManager#removeViewImmediate(mDecor)移除.

注意: 普通Dialog必須採用ActivityWindow

Toast的Window創建過程

  1. Toast的工作過程比Dialog稍顯複雜,Toast具有定時取消這一功能(採用Handler).
  2. Toast內部有兩類IPC過程.一類,Toast->NotificationManagerService,二類,NotificationManagerService -> Toast#TN
  3. Toast屬於系統Window,內部視圖可以通過setView來指定
  4. Toast提供show和cancel方法,他們是一個IPC過程,都是通過NMS完成
  5. TN是一個Binder類,NMS回調TN來處理Toast的show和cancel
  6. 由於TN運行在Binder線程池中,因此需要使用Handler(必須在有Looper的線程中彈出Toast)
  7. 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的窗口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章