8.理解Window和WindowManager

Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。

WindowManager.LayoutParams flags参数表示Window的属性:

  • FLAG_NOT_FOCUSABLE,表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window。
  • FLAG_NOT_TOUCH_MODAL,在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件。
  • FLAG_SHOW_WHEN_LOCKED,开启此模式可以让Window显示在锁屏的界面上。

Type参数表示Window的类型,Window有三种类型,分别是应用Window、子Window和系统Window。应用类Window对应着一个Activity。子Window不能单独存在,它需要附属在特定的父Window之中,比如常见的一些Dialog就是一个子Window。系统Window是需要声明权限在能创建的Window,比如Toast和系统状态栏这些都是系统Window。

Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖在层级小的Window的上面,这和HTML中的z-index的概念是完全一致的。在三类Window中,应用Window的层级范围是1~99,子Window的层级范围是1000~1999,系统Window的层级范围是2000~2999,这些层级范围对应着WindowManager.LayoutParams的type参数。

WindowManager所提供的功能很简单,常见的只有三个方法,即添加View,更新View和删除View。

Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。

WindowManagerGlobal内部有几个列表比较重要:

        private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

mViews存储的是所有Window所对应的View,mRoots存储的是所有Window所对应的ViewRootImpl,mParams存储的是所有Window所对应的布局参数,而mDyingViews则存储了那些正在被删除的View对象,或者说是那些已经调用removeView方法但是删除操作还未完成的Window对象。

通过WindowSession来完成Window的添加过程,mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是Window的添加过程是一次IPC调用。在WindowManagerService内部会为每一个应用保留一个单独的Session。

Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的。removeView的逻辑很清晰,首先通过findViewLocked来查找待删除的View的索引,然后再调用removeViewLocked来做进一步的删除。removeViewLocked是通过ViewRootImpl来完成删除操作的。在WindowManager中提供了两种删除接口removeViewremoveViewImmediate,它们分别表示异步删除和同步删除。异步删除的情况,具体的删除操作由ViewRootImpl的die方法来完成。在异步删除的情况下,die方法至少发送了一个请求删除的消息,这个时候View并没有完成删除操作,最后会将其添加到mDyingViews中,mDyingViews表示待删除的View列表。在doDie内部会调用dispatchDetachedFromWindow方法,真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。dispatchDetachedFromWindow方法主要做四件事:

  • 垃圾回收相关的工作,比如清除数据和消息、移除回调。
  • 通过Session的remove方法删除Window,这同样是一个IPC过程,最终会调用WindowManagerService的removeWindow方法。
  • 调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。对于onDetachedFromWindow(),当View从Window中移除的时候,这个方法就会被调用,可以在这个方法内部做一些资源回收的工作,比如终止动画、停止线程等。
  • 调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。

Window的更新过程,WindowManagerGlobal的updateViewLayout方法,首先它需要更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams,这一步是通过ViewRootImpl的setLayoutParams方法来实现的。在ViewRootImpl中会通过scheduleTraversals方法来对View重新布局,包括测量、布局、重绘三个过程。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,它同样是一个IPC过程。

显示和隐藏Toast都需要通过NotificationManagerService来实现,由于NMS运行在系统的进程中,所以只能通过远程调用的方式来显示和隐藏Toast。需要注意的是TN这个类,它是一个Binder类,在Toast和NMS进行IPC的过程中,当NMS处理Toast的显示或隐藏请求时会跨进程回调TN中的方法,这个时候由于TN运行在Binder线程池中,所以需要通过Handler将其切换到当前线程中。这里的当前线程是指发送Toast请求所在的线程。由于这里使用了Handler,所以这意味着Toast无法再没有Looper的线程中弹出,这是因为Handler需要使用Looper才能完成切换线程的功能。

Toast的显示和隐藏过程实际上是通过Toast中的TN这个类来实现的,它有两个方法show和hide,分别对应Toast的显示和隐藏。由于这两个方法是被NMS以跨进程的方式调用的,隐藏它们运行在Binder线程池中。Toast的show方法调用NotificationManagerService.mService.enqueueToast方法,来获取token,获取到token后传递给TN在handleShow方法中赋值给WindowManager.LayoutParams.token。

results matching ""

    No results matching ""