Window和WindowManager解析

 这几天阅读了《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<View> mViews = new ArrayList<View>();
    // 存在window所对应的viewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    // 存储了所有window对应的布局参数
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    // 存储了那些正在被删除的view对象,调用了removeVIew,但是没有完成的
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

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的添加过程

    // 添加啦!!!!!!!!这是通过ViewRootImplsetView来完成
    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);
    }

总结

 这篇博文主要是读书笔记式的总结,本来想写一些自己的东西,但是研究的太浅,并且语言组织上还是有不足,以后还需要注意和继续努力啦.
 本文也在我的独立博客同时发布.

你可能感兴趣的:(android,window)