8 理解Window和WindowMananger

Window是一个抽象类,具体实现是PhoneWindow。不管是ActivityDialogToast它们的视图都是附加在Window上的,因此Window实际上是View的直接管理者
WindowManager是外界访问Window的入口,通过WindowManager可以创建Window,而Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。

8.1 Window和WindowManager

下面代码演示了通过WindowManager添加Window的过程:

mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mFloatingButton = new Button(this);
            mFloatingButton.setText("click me");
            mLayoutParams = new WindowManager.LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
                    PixelFormat.TRANSPARENT);
            mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
            mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
            mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
            mLayoutParams.x = 100;
            mLayoutParams.y = 300;
            mFloatingButton.setOnTouchListener(this);
            mWindowManager.addView(mFloatingButton, mLayoutParams);

WindowManager的flags和type这两个属性比较重要:
Flags代表Window的属性,控制Window的显示特性

  1. FLAG_NOT_FOCUSABLE 在此模式下,Window不需要获取焦点,也不需要接收各种输入事件,这个标记同时会启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层具有焦点的Window。
  2. FLAG_NOT_TOUCH_MODAL 在此模式下,系统将当前Window区域以外的点击事件传递给底层的Window,当前Window区域内的单击事件则自己处理。一般需要开启此标记。
  3. FLAG_SHOW_WHEN_LOCKED 开启此模式Window将显示在锁屏界面上。

type参数表示Window的类型。

  1. 应用Window
  2. 子Window 如Dialog
  3. 系统Window 如Toast和系统状态栏

Window是分层的,每个Window对应一个z-ordered,层级大的会覆盖在层级小的上面,和HTM的z-index概念一样。在三类Window中,应用Window的层级范围是1~99,子Window的层级范围是1000~1999,系统Window的层级范围是2000~2999,这些值对应WindowManager.LayoutParams的type参数。一般系统Window选用TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR同时需要权限<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />)。

WindowManager提供的功能很简单,常用的只有三个方法:

  1. 添加View
  2. 更新View
  3. 删除View

这个三个方法定义在ViewManager中,而WindowManager继承了ViewManager。

public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
     * errors, such as adding a second view to a window without removing the first view.
     * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
     * secondary {@link Display} and the specified display can't be found
     * (see {@link android.app.Presentation}).
     * @param view The view to be added to this window.
     * @param params The LayoutParams to assign to view.
     */
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

8.2 Window的内部机制

Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。因此Window并不是实际存在的,它是以View的形式存在的。所以WindowManager的三个方法都是针对View的,说明View才是Window存在的实体。在实际使用中无法直接访问Window,必须通过WindowManager来访问Window。

8.2.1 Window的添加过程

8.2.2 Window的删除过程

8.2.3 Window的更新过程

以上三个小节都是通过WindowManager的真正实现类——WindowManagerImpl类的三大方法的源码来对Window的内部机制进行分析。
具体点说就是,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给WindowManagerGlobal处理。然后在WindowManagerGlobal内部都是通过ViewRootImpl里的一个Binder对象mWindowSessionIWindowSession类型)进行IPC调用WindowManagerService进行Window的三大操作。

8.3 Window的创建过程

由之前的分析可以知道,View是Android中视图的呈现方式,但是View不能单独存在,必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window。这些视图包括:Activity、Dialog、Toast、PopUpWindow等等。

8.3.1 Activity的Window创建过程

8.3.2 Dialog的Window创建过程

8.3.3 Toast的Window创建过程

由于以上三个小节有大量源码的分析,脱离了具体的代码片段不方便做笔记。只做简单概括:

  1. 在创建视图并显示出来时,首先是通过创建一个Window对象,然后通过WindowManager对象的addView(View view, ViewGroup.LayoutParams params);方法将contentView添加到Window中,完成添加和显示视图这两个过程。
  2. 在关闭视图时,通过WindowManager来移除DecorView,mWindowManager.removeViewImmediate( view);
  3. Toast比较特殊,具有定时取消功能,所以系统采用了Handler,内部有两类IPC过程:
    1. Toast访问NotificationManagerService
    2. NotificationManagerService回调Toast里的TN接口

显示和隐藏Toast都通过NotificationManagerService(NMS)来实现,而NMS运行在系统进程中,所以只能通过IPC来进行显示/隐藏Toast。而TN是一个Binder类,在Toast和NMS进行IPC的过程中,当NMS处理Toast的显示/隐藏请求时会跨进程回调TN中的方法,这时由于TN运行再Binder线程池中,所以需要通过Handler将其切换到当前线程(即发起Toast请求所在的线程),然后通过WindowManager的addView/removewView方法真正完成显示和隐藏Toast。

本章节对源码的分析侧重的是整体流程,避免深入代码逻辑无法自拔的情形。

results matching ""

    No results matching ""