Android的Window和WindowManager

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实现的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值