这几天阅读了《Android开发艺术探索》的关于Window和WindowManager的章节,特此写一片博文来整理和总结一下学到的知识.
说到Window,大家都会想到所有的视图,包括Activity,Dialog,Toast,它们实际上都是附加在Window上的,Window是这些视图的管理者.今天我们就来具体将一下这些视图是如何附加在Window上的,Window有是如何管理这些视图的.
Window的属性和类别
当我们通过WindowManager添加Window时,可以通过WindowManger.LayoutParams来确定Window的属性和类别.其中Flags参数标示Window的属性,我们列出几个比较常见的属性:
FLAG_NOT_FOCUSABLE这个参数表示Window不需要获取焦点,也不需要接收任何输入事件FLAG_NOT_TOUCH_MODAL这个参数表示当前Window区域之外的点击事件传递给底层Window,区域之内的点击事件自己处理,一般默认开启FLAG_SHOW_WHEN_LOCKED这个属性可以让Window显示在锁屏界面上
Window不仅有属性,还有类型.Type参数表示Window的类型,分别为应用Window(activity对应的),子window(dialog对应的),和系统Window(Toast和系统通知栏).Window是分层的,每个window都有z-ordered,层级大的window会覆盖层级小的window,其大小关系为系统window>子window>应用window.所以系统window总会显示在最上边,但是使用系统window是需要声明相应的权限的.这一点需要注意.WindowManager
我们先来看一下WindowManager的接口,对其接口函数的了解有助于我们更好的理解Window的类别和属性.
WindowManger实现了ViewManager这个接口,所提供的主要函数只有三个:public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);而且通过阅读源码,我们会发现所有的操作都是交由WindowManagerGloalal来进行.之后的小节我会依次介绍.这一节先讲一下它的比较重要的成员变量.
// 存储所有window所对应的view private final ArrayList mViews = new ArrayList(); // 存在window所对应的viewRootImpl private final ArrayList mRoots = new ArrayList(); // 存储了所有window对应的布局参数 private final ArrayList mParams = new ArrayList(); // 存储了那些正在被删除的view对象,调用了removeVIew,但是没有完成的 private final ArraySet mDyingViews = new ArraySet();Window的添加过程
这是WindowManagerGlobal的对应接口
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)创建ViewRootImpl,并将View添加到相应的列表中
// 创建ViewRootImpl,然后将下述对象添加到列表中 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams);//设置Params mViews.add(view);//window列表添加 mRoots.add(root);//ViewRootImpl列表添加 mParams.add(wparams);//布局参数列表添加通过ViewRootImpl来更新界面完成window的添加过程
// 添加啦!!!!!!!!这是通过ViewRootImpl的setView来完成 root.setView(view, wparams, panelParentView);在ViewRootImpl的setView函数中,会调用requestLayout来完成异步刷新,然后在requestLayout
中调用scheduleTraversals来进行view绘制.@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); // 实际View绘制的入口 } }最后通过WindowSession来完成Window的添加过程,它是一个Binder对象,通过IPC调用来添加window.
所以,Window的添加请求就交给WindowManagerService去处理,在其内部为每个应用保留一个单独的Session.Window的删除过程
public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { int index = findViewLocked(view, true); //先找到view的index View curView = mRoots.get(index).getView(); removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); } }removeView先通过findViewLocked来查找待删除的View的索引,然后用removeViewLocked来做进一步删除.
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); //获得当前的view的viewRootImpl View view = root.getView(); if (view != null) { //先让imm下降 InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } boolean deferred = root.die(immediate); //die方法只是发送一个请求删除的消息之后就就返回 if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view);//加入dyingView } } }在WindowManager中提供了两种删除接口removeVIew()和removeViewImmediate(),它们分别表示异步和同步删除.而异步操作中会调用die函数,来发送一个MSG_DIE消息来异步删除,ViewRootImpl的Handler会调用doDie(),而如果是同步删除,那么就直接调用doDie(),然后在removeView函数中把View添加到mDyingViews中.
Window的更新
public void updateViewLayout(View view, ViewGroup.LayoutParams params) { ..... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; view.setLayoutParams(wparams); synchronized (mLock) { int index = findViewLocked(view, true); ViewRootImpl root = mRoots.get(index); mParams.remove(index); mParams.add(index, wparams); root.setLayoutParams(wparams, false);//这是主要的方法 } }在setLayoutParams中会调用scheduleTraversals来重新绘制.
Window的创建过程
不同类型的Window的创建过程不同,这里我只来讲一下Activity的Window的创建过程.在Window的启动过程中,会调用attach()函数来为其关联运行过程中所依赖的一系列上下文环境变量.
mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this);Window对象是通过PolicyManager的makeNewWindow方法实现的,由于Activity实现了Window的Callback接口,因此当Window接收到外界的状态改变时就会回调Activity的对应方法.而我们去追寻Window的具体实现类,会发现它就是PhoneWindow,而Activity中最常用的setContentView方法的具体操作都是在PhoneWindow的相应方法中实现的.
如果没有DecorView,那么就创建它.DecorView是一个FrameLayout,是Activity中的顶级View,一般包括标题栏和内容栏,而且内容栏的id为android.R.id.content,而DecorView的创建过程由installDecor完成,内部会通过generateDecor方法来直接创建DecorView
if (mContentParent == null) { //如何没有DecorView,那么就新建一个 installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (mDecor == null) { mDecor = generateDecor(); //直接new出一个DecorView返回 ...... } if (mContentParent == null) { //[window] 这一步也是很重要的. mContentParent = generateLayout(mDecor); ....... } .......而在generateLayout中
//根据不同的style生成不同的decorview啊 View in = mLayoutInflater.inflate(layoutResource, null); // 加入到deco中,所以应该是其第一个child decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; //给DecorView的第一个child是mContentView // 这是获得所谓的content ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);将View添加到DecorView的mContentParent中,这步只需要一条语句就可,具体内部细节就不说啦.
//第二步,将layout添加到mContentParent mLayoutInflater.inflate(layoutResID, mContentParent);然后就是回调Acitivity的onContentChanged方法通知Activity视图已经改变了.
final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); }经历了上述三个步骤,DecorView已经创建并初始化完毕,并且Activity的布局文件已经成功添加到DecorView的mContentParent中,但是DecorView并没有添加到WindowManager中去,也无法接收外界的输入,只有到Acitivity的makeVisible()被调用时,DecorView才真正完成了添加和显示过程.
//DecorView正式添加并显示 void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }总结
这篇博文主要是读书笔记式的总结,本来想写一些自己的东西,但是研究的太浅,并且语言组织上还是有不足,以后还需要注意和继续努力啦.