Android的Window和WindowManager

来源:互联网 发布:传真服务器软件 编辑:程序博客网 时间:2024/06/06 20:01

Window和Windowmanager

window是Android视图View显示的容器,activity、Dialog、Toast中的View都是通过window来呈现的,它是一个抽象类,只有一个实现类phonewindow。windowmanager是访问window的唯一入口,window内添加、删除、更新View的操作都由windowmanager来管理,WindowManager的功能是通过WindowManagerService这个系统服务提供的,两者相互调用是一个IPC的过程。

Window

window其实是一个抽象的概念,并没有在屏幕上有具体的存在,只是作为View的容器有window的属性。添加一个window其实是通过windowManager的addView()来实现的,该方法可以将View以window的形式显示出来。

Windowmanager.LayoutParams 有两个重要的属性flags和type。

flag 表示window的属性,可以控制window的显示特性,有很多,参考开发文档。

比如FLAG_SHOW_WHEN_LOCKED可以将window显示在锁屏界面上。

Type 参数表示window的类型,三类:应用Window,子Window,系统Window。window的类型影响window的显示层级。

应用window对应着activity层级1-99,子window常见的有dialog层级1000-1999,子Window必须附属在特定的父Window上,系统Window需要声明权限才能创建,层级2000-2999,toast,系统状态栏就是系统window。

层级越大就可以在越上层显示。

WindowManager

Windowmanager是用来操作window的类有添加,删除,更新。这些操作实际操作的都是window内部的View

Window的内部机制

Window添加到屏幕

首先从添加Window开始,添加Window其实是addView()即添加View,通过windowmanager的addView()实现,WindowManager的实现类是WindowManagerImp类,而WindowManagerImp的addView()调用了windowManagerGlobal()的addView(),类似ContextWraper调用ContextImp类的方法。可以推测windowmanager的其他两个removeview()和update也是同样的实现方式。

看看WindowManagerGlobal的addView()方法

public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        if (display == null) {            throw new IllegalArgumentException("display must not be null");        }        if (!(params instanceof WindowManager.LayoutParams)) {            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");        }        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;        if (parentWindow != null) {            parentWindow.adjustLayoutParamsForSubWindow(wparams);        }        ViewRootImpl root;        View panelParentView = null;      。。。。。。。。。。            root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            if (mViews == null) {                index = 1;                mViews = new View[1];                mRoots = new ViewRootImpl[1];                mParams = new WindowManager.LayoutParams[1];            } else {                index = mViews.length + 1;                Object[] old = mViews;                mViews = new View[index];                System.arraycopy(old, 0, mViews, 0, index-1);                old = mRoots;                mRoots = new ViewRootImpl[index];                System.arraycopy(old, 0, mRoots, 0, index-1);                old = mParams;                mParams = new WindowManager.LayoutParams[index];                System.arraycopy(old, 0, mParams, 0, index-1);            }            index--;            mViews[index] = view;            mRoots[index] = root;            mParams[index] = wparams;        }        // do this last because it fires off messages to start doing things        try {            root.setView(view, wparams, panelParentView);        } catch (RuntimeException e) {            // BadTokenException or InvalidDisplayException, clean up.            synchronized (mLock) {                final int index = findViewLocked(view, false);                if (index >= 0) {                    removeViewLocked(index, true);                }            }            throw e;        }    }
 mViews[index] = view;            mRoots[index] = root;            mParams[index] = wparams;

WindowManagerglobal还将View,viewRoot,params存入了一个数组,所以每个View和ViewRoot是一一对应的,windowmanager进行add,remoteview和update时就可以将相关的参数同时添加和移除。

看到addView是通过ViewRootImp来实现绘制View的,ViewRootImp的setView()方法会绘制View,这在之前的文章中提到过View绘制原理提到的View的绘制是有ViewRoot实现的,ViewRoot的实现类是ViewRootImp。

绘制完View之后通过WindowSession完成添加window到屏幕上,其实现类是Session,是个Binder类型,用于与WindowManagerService进行进程通信。

总结一下:添加的过程.

WindowManager->WindowManagerImp->WindowManagerGlobal.addView()->ViewRootImp.setView()绘制View->Session->WindowManagerService系统进程添加Window

Window删除

直接看Windowmanagerglobal的removeView()

 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 curView = removeViewLocked(index, immediate);            if (curView == view) {                return;            }            throw new IllegalStateException("Calling with view " + view                    + " but the ViewAncestor is attached to " + curView);        }    }
private int findViewLocked(View view, boolean required) {        if (mViews != null) {            final int count = mViews.length;            for (int i = 0; i < count; i++) {                if (mViews[i] == view) {                    return i;                }            }        }        if (required) {            throw new IllegalArgumentException("View not attached to window manager");        }        return -1;    }

直接找到View的index,这样就可以将数组中的View移除,将被移除的对象会被放入另一个数组中。删除操作是用ViewRootImp的doDie()方法实现的会调用dispatchDetachedFromWindow()方法,该方法通过Session与WindowmanagerService通信,然后调用View的onDetachedFromWindow(),我们在自定义View时,可以在这个方法中我们进行结束动画,停止线程等操作。

更新Window

大致流程和上面类似,更新会重置View的Layoutparams。最终还是由WMS来实现的。

activity的Window的创建过程

首先在启动activity时,会创建activity的window对象,并设置其回调接口,activity实现了window的回调接口callback,所以activity可以处理window相关的事件,比如dispatchTouchEvent()方法。

activity的View是怎么添加到window上的呢?

从activity的setcontentview开始,

public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }

调用了Window的setContentView(),window的实现类是PhoneWindow,

 @Override    public void setContentView(int layoutResID) {        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window        // decor, when theme attributes and the like are crystalized. Do not check the feature        // before this happens.        if (mContentParent == null) {            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);        } else {            mLayoutInflater.inflate(layoutResID, mContentParent);        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }    }

首先生成DecorView,将Content添加到DecorView上,执行5.0以上的新增的过渡动画,将布局文件添加到Content中,最后回调window的callback的方法,oncontentchanged(),表示activity的内容已改变。

目前为止还没有将DecorView添加到window中,这步是在Activitythread的HandleResumeActivity()中执行的,回调activity的onResume()并调用windowmanager的addView()将DecorView添加到window中。

activity的window创建完成了布局文件也添加到了Window中。

Toast、Diaolog、PopupWindow、菜单、及状态栏都是通过window实现的。

2 0