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艺术开发探索》
新闻热点
疑难解答