Window是一個抽象的概念,每一個Window都對應一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯系。
在實際使用中無法直接訪問Window,對Window的訪問必須通過Windowmanager。
Window實際上是View的直接管理者
Android中所有的視圖都是通過Window來呈現的,不管是Activity、Dialog還是Toast,它們的視圖實際上是附加在Window上的。對應著一個Activity
子Window不能單獨存在,它需要附屬在特定的父Window之中,比如Dialog
需要聲明權限才能創建的Window,比如Toast和系統狀態欄
層級大的Window會覆蓋層級小的Window,層級對應于WindowManager.LayoutParams的type參數。
應用Window層級范圍:1~999 子Window的層級范圍:1000~1999 系統Window的層級范圍:2000~2999
Window通過WindowManager來訪問,WindowManager是一個接口,它的真正實現是WindowManagerImpl。而WindowmanagerImpl的增刪更新View都交由WindowManagerGlobal來處理。
public final class WindowManagerImpl implements WindowManager { PRivate final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @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); }}需要了解的WindowManagerGlobal的幾個重要的參數
//存儲所有Window所對應的Viewprivate final ArrayList<View> mViews = new ArrayList<View>();//存儲所有Window所對應的ViewRootImplprivate final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();//存儲所有Window所對應的布局參數private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();//存儲即將被刪除的View對象 (已經調用removeView方法但是刪除操作還未完成的Window對象)private final ArraySet<View> mDyingViews = new ArraySet<View>();Window的添加過程通過WindowManager的addView來實現
WindowManagerGlobal的addView()
在來看ViewRootImpl#setView(),Window的添加請求最終交給WindowManagerService處理。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { //... //[重要方法] requestLayout-> ... -> performTraversals -> 開始measure、layout、draw三大流程 requestLayout(); //... try { //通過Windowsession最終完成Window的添加 //mWindowSession是IWindowSession,它是一個Binder對象 [ipC調用] //mWindowSession.addToDisplay -> WindowmanagerService.addWindow mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setaccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } //...}再來看removeViewLocked
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } 具體的刪除操作 boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } }}再來看die方法。如果是同步刪除,立即調用doDie(),如果是異步調用,會通過Handler發送消息調用doDie()。
boolean die(boolean immediate) { // Make sure we do execute immediately if we are in the middle of a traversal or the damage // done by dispatchDetachedFromWindow will cause havoc on return. //立即刪除 -> 同步刪除 (不推薦,容易發生意外) if (immediate && !mIsInTraversal) { doDie(); return false; } if (!mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(TAG, "Attempting to destroy the window while drawing!/n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } //異步刪除 mHandler.sendEmptyMessage(MSG_DIE); return true;}再來看doDie()
void doDie() { //... //真正刪除View dispatchDetachedFromWindow(); //...}dispatchDetachedFromWindow主要做4件事
垃圾回收相關的工作,比如清除數據和消息,移除回調。 通過Session的remove方法刪除Window:mWindowSession.remove(mWindow),這同樣是一個IPC過程,最終會調用WindowManagerService的removeWindow方法。 調用View的dispatchDetachedFromWindow方法,在內部會調用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。調用WindowManagerGlobal的doRemoveView方法刷新數據,包括mRoots、mParams以及mDyingViews,需要將當前Window所關聯的這三個對象從列表中刪除。onDetachedFromWindow() 當View從Window中移除時,這個方法就會被調用。 可以在這個方法內部做一個資源回收工作,比如終止動畫、停止線程。
window的更新通過WindowManagerGlobal#updateViewLayout()來完成,內部通過調用VIewRootImpl的setLayoutParams(),然后經過一些列的方法,最終會開始measure、layout、draw三大流程,從而實現window的更新。
參考 《Android藝術開發探索》
新聞熱點
疑難解答