Android进阶知识(二十):理解Window和WindowManager

Android进阶知识(二十):理解Window和WindowManager

  Window表示一个窗口的概念,其实际上是View的直接管理者;它是一个抽象类,具体实现是PhoneWindow。Window的创建可以通过WindowManager来完成,WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程

一、Window和WindowManager

  通过WindowManager添加Window的代码如下。

mFloatingButton = new Button(this);
mFloatingButton.setText("button");
mLayoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT,
                                         LayoutParms.WRAP_CONTENT,
                                         0, 0, PixelFormat.TRANSPARENT);
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
        | LayoutParams.FLAG_NOT_FOCUSABLE
        | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mWindowManager.addView(mFloatingButton, mLayoutParams); 

  上述代码将一个Button添加到屏幕(100,300)的位置上。其中WindowManager.LayoutParams中的flags参数表示Window属性,type表示Window类型。flags常见属性如下表所示。

flags参数选项介绍
FLAG_NOT_FOCUSABLE表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window
FLAG_NOT_TOUCH_MODAL系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。
FLAG_SHOW_WHEN_LOCKED开启此模式可以让Window显示在锁屏的界面上

  Window类型有三种,分别为应用Window、子Window和系统Window。相对应的Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖在层级小的Window上面。具体见下表。

Window类型描述层级等级
应用Window对应一个Activity层级范围:1~99
子Window不能单独存在,需要附属在特定的父Window之中,例如Dialog层级范围:1000~1999
系统Window需要声明权限才能创建的Window,比如Toast和系统状态栏层级范围:2000~2999

  需要注意的是,系统类型的Window是需要检查权限的,需要添加权限:。
  WindowManager所提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager。WindowManager操作Window的过程更像是在操作Window中的View

public interface ViewManager {
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

二、Window的内部机制

  Window是一个抽象的概念,每个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此Window并不是实际存在的,View才是Window存在的实体
在这里插入图片描述
  在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。为了分析Window内部机制,从Window的添加、删除以及更新说起。

  1. Window的添加过程

  Window的添加过程需要通过WindowManager的addView实现,WindowManager是一个接口,其真正实现是WindowManagerImpl类。在该类中Window的三大操作全部交给WindowManagerGlobal来处理,这种模式叫做桥接模式
在这里插入图片描述
  WindowManagerGlobal的addView方法主要分为如下几步:
  1)检查参数是否合法,如果是子Window那么还需要调整一些布局参数
  2)创建ViewRootImpl并将View添加到列表中
  在WindowManagerGlobal内部有几个列表变量比较重要,如下表所示。

列表类型存储内容
mViewsArrayList<View>所有Window所对应的View
mRootsArrayList<ViewRootImpl>所有Window所对应的ViewRootImpl
mParamsArrayList<WindowManager.LayoutParams>所有Window所对应的布局参数
mDyingViewsArrayList<View>存储正在被删除的View对象,或者说已经调用removeView方法但是删除操作还未完成的Window对象

  在addView中通过如下方式将Window的一系列对象添加到列表中。

root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);

mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

  3)通过ViewRootImpl来更新界面并完成Window的添加过程
  该步骤通过ViewRootImpl的setView完成,在setView内部会调用requestLayout来完成异步刷新请求。具体流程如下。
在这里插入图片描述
  由此可见Window的添加过程是一次IPC调用,最终Window的添加请求会交给WindowManagerService处理。
在这里插入图片描述

  1. Window的删除过程

  Window的删除过程和添加过程一致,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal实现。具体流程如下。
在这里插入图片描述
  removeViewLocked是通过ViewRootImpl的die方法来完成的。在WindowManager中提供了两种删除接口removeView(异步删除)和removeViewImmediate(同步删除,一般不使用)
  在异步删除的情况下,die方法只是发送一个MSG_DIE的消息就立刻返回了,这个时候View并没有完成删除操作,所以最后会将其添加到mDyingViews中。

boolean die(boolean immediate) {
    // 如果我们在遍历中途,请确保立即执行,
    // 否则dispatchDetachedFromWindow造成的损坏将在返回时造成破坏
    if (immediate && !mIsInTraversal) {
        doDie();
        return false;
    }
    if (!mIsDrawing) {
        destroyHardwareRenderer();
    } else {
        Log.e(...);
    }
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}

  两种删除方法的区别在于,异步删除只是发送一个MSG_DIE消息,Handler会处理此消息并调用doDie方法;而同步删除不发消息直接调用doDie方法。在doDie方法内部会调用dispatchDetachedFromWindow方法,这是真正的删除逻辑所在,其主要任务如下:

  • 垃圾回收相关的工作。
  • 通过Seesion的remove方法删除Window:IPC过程,最终调用WindowManagerService的removeView方法。
  • 调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetachedFromWindow以及onDetachedFromWindowInternal。
  • 调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews。
  1. Window的更新过程

  Window的更新过程由WindowManagerGlobal的updateViewLayout实现,其逻辑比较简单,首先更新View的LayoutParams并替换掉老的LayoutParams,接着更新ViewRootImpl中的LayoutParams(ViewRootImpl.setLayoutParams)。

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    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);
    }
}

  在ViewRootImpl中通过scheduleTraversals方法对View重新进行测量、布局、重绘,除此之外,还会通过WindowSession来更新Window的视图,这个过程最终通过WindowManagerService的relayoutWindow()来实现,同样是一个IPC过程
在这里插入图片描述

参考资料:《Android开发艺术探索》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值