概述

Window表示窗口的概念,他是一个抽象类,他的真正实现类是PhoneWindow,WindowManager用来对Window进行管理,是外接访问Window的入口,Window操作的具体实现是在WindowManagerService中,WindowMager和WindowManagerService交互是IPC的过程。Android中所有的视图都是附加在Window上上呈现的,不管Activity,Dialog,Toast,他们的视图都是附加在Window上的,因此Window实际上是View的直接管理者。
下面我们来详细的了解Window:

WindwoMagaer来添加一个Window

public static void addView(Context context){
        Button button = new Button(context);
        button.setText("addWindow");
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                0,
                0,
                PixelFormat.TRANSPARENT);

        layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
        layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        layoutParams.gravity= Gravity.LEFT|Gravity.TOP;
        layoutParams.x=100;
        layoutParams.y=300;
        WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
        //WindowManager windowManager = context.getWindowManager();
        windowManager.addView(button,layoutParams);
    }

这段代码可以添加一个Window,位置在(100,300)处,这里面有俩个参数比较重要分别是,type和flag,下面分别介绍一下这俩个参数:

TYPE 窗口的属性

type参数表示Window的类型,Window有三种类型,分别是Application Window(应用窗口),Sub Window(子窗口)和System Window(系统窗口),每个大类型又包含多个小类型,他们都定义在WindowMager的静态内部类LayoutParams中,下面对这三种类型进行讲解。

  • Application Window(应用窗口)
    Activity就是典型的应用窗口,应用窗口包含的类型如下:
public static final int TYPE_BASE_APPLICATION   = 1;
public static final int TYPE_APPLICATION        = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int LAST_APPLICATION_WINDOW = 99;

应用窗口就包括了以上几中类型,其中最上方是起始值,最下方是结束值,也就是说应用窗口的Type值的范围是1-99,这个数值的大小涉及窗口的层级。

  • Sub Window(子窗口)
    子窗口不能够独立存在,要依附在其他窗口上才行,PopupWindow就属于子窗口,子窗口的定义类型如下:
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;

可以看出子窗口的type值范围是1000-1999。

  • System Window (系统窗口)
    Toast,输入法窗口,系统音量条窗口,系统错误窗口,都属于系统窗口,系统窗口的类型定义如下:
public static final int FIRST_SYSTEM_WINDOW     = 2000;
public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
public static final int LAST_SYSTEM_WINDOW      = 2999;

窗口的显示次序

上面介绍的Type值越大,就意味着靠用户越近,很显然系统的窗口是最大的,他在应用窗口和子窗口的上方。

ViewManager类

WindowMagaer 所提供的功能很简单,只有常用的三个方法即,添加View,更新View,删除View,这个三个方法定义在ViewManager中,而WindowManager继承自ViewManager

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,只有通过WindowManager才能访问。

Window的添加过程

  • WindowManager–addView

Window的添加是通过WindowManager的addView方法实现的,我们WindowManager##addView方法作为入口来分析,WindowMagager是一个接口,真正的实现在WindowManagerImpl。

  • WindowManagerImpl–addView

frameworks\base\core\java\android\view\WindowManagerImpl.java

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    android.util.SeempLog.record_vg_layout(383,params);
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
  • WindowManagerGlobal–addView

frameworks\base\core\java\android\view\WindowManagerGlobal.java

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");
    }
........
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(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.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }                
            
            
        }
  • ViewRootImpl–setView

ViewRootImpl有很多的职责
View树的根,并管理View树
触发View的测量,布局和绘制
输入时间的中转站
管理Surface
负责与WMS通信
我们继续看一下ViewRootImpl的setView方法

frameworks\base\core\java\android\view\ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        ......
        // Schedule the first layout -before- adding to the window
        // manager, to make sure we do the relayout before receiving
        // any other events from the system.
        requestLayout();
        
        if ((mWindowAttributes.inputFeatures
                & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
            mInputChannel = new InputChannel();
        }
        mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
        try {
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                    mTempInsets);
            setFrame(mTmpFrame);

这个方法首先会调用requestLayout方法来完成一部刷新请求。

public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

scheduleTraversals实际是View绘制的入口。

  • Session–addToDisplay

frameworks\base\services\core\java\com\android\server\wm\Session.java

然后调用mWindowSession.addToDisplay方法,mWindowSession是一个IWindowSession类型的,是一个Binder对象,用于进程间通信,也就是说addToDisplay方法其实是运行在WMS所在的进程system_server进程。

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
        Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
            outInsetsState);
}

addToDisplay方法内部调用了WMS的addWindow方法并将自身也就是Session传入了进去,每个应用程序都会有一个Session,WMS会用ArrayList来保存起来,这样所有的工作都交给了WMS来做。

  • WindowManagerService–addWindow

WMS会为这个添加的窗口分配Surface,并确定窗口的显示次序,负责显示界面的是画布Surface,而不是窗口本身,WMS会把Surface交给SurfaceFlinger处理,SurfaceFlinger会把这些Surface混合并绘制到屏幕上

下图为addWindow的时序图:

android开发浅谈之Window和WindowManager_java

6.Window的更新过程

Window的更新过程和添加过程是类似的,需要调用WindowManager的updateViewLayout方法.

  • WindowManager–updateViewLayout

Window的更新过程是通过WindowManager的updateViewLayout方法实现的,我们WindowManager#updateViewLayout方法作为入口来分析,WindowMagager是一个接口,真正的实现在WindowManagerImpl。

  • WindowManagerImpl–updateViewLayout

frameworks\base\core\java\android\view\WindowManagerImpl.java

public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    android.util.SeempLog.record_vg_layout(384,params);
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
}

WindowManagerGlobal–updateViewLayout
frameworks\base\core\java\android\view\WindowManagerGlobal.java

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–setLayoutParams

frameworks\base\core\java\android\view\ViewRootImpl.java

void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
        synchronized (this) {
            ......
            if (newView) {
                mSoftInputMode = attrs.softInputMode;
                requestLayout();
            }

            // Don't lose the mode we last auto-computed.
            if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                    == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
                mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
                        & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                        | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
            }

            mWindowAttributesChanged = true;
            scheduleTraversals();

调用ViewRoot的setLayoutParams方法,将更新的参数设置到ViewRootImpl中,setLayoutParams方法最终会调用ViewRootImpl的scheduleTraversals方法,我们看下这个方法.

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

mChoreographer翻译为编舞者,用于接受系统的VSync信号,在下一个帧渲染时控制一些操作,mChoreographer的postCallback方法用于添加回调,这个添加的回调,将在下一帧渲染时执行,这个添加的回调指的是TraversalRunnable类型的mTraversalRunnable,如下:

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

这个方法内部调用了doTraversal

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

这个方法又调用了performTraversals,这个方法中更新了Window的视图,并且完成了View的绘制流程,measure,layout,draw,这样就完成了View的更新.

下图为updateViewLayout的时序图:

android开发浅谈之Window和WindowManager_子窗口_02

参考资料:

(1)Android 理解Window和WindowManager

(2)Android App启动过程

(3)Android View源码解读:浅谈DecorView与ViewRootImpl

(4)Android View 测量流程(Measure)完全解析

(5)Android View 绘制流程(Draw) 完全解析

(6)Android View 布局流程(Layout)完全解析