欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

android开发浅谈之Window和WindowManager

程序员文章站 2022-03-12 21:33:21
android开发浅谈之Window和WindowManager概述WindwoMagaer来添加一个WindowTYPE 窗口的属性ViewManager类Window的内部机制Window的添加过程6.Window的更新过程参考资料:概述Window表示窗口的概念,他是一个抽象类,他的真正实现类是PhoneWindow,WindowManager用来对Window进行管理,是外接访问Window的入口,Window操作的具体实现是在WindowManagerService中,WindowMager和...

概述

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

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

参考资料:

(1)Android 理解Window和WindowManager
https://blog.csdn.net/qq_34760508/article/details/103086195
(2)Android App启动过程
https://juejin.cn/post/6844903933802987528
(3)Android View源码解读:浅谈DecorView与ViewRootImpl
https://blog.csdn.net/a553181867/article/details/51477040
(4)Android View 测量流程(Measure)完全解析
https://blog.csdn.net/a553181867/article/details/51494058
(5)Android View 绘制流程(Draw) 完全解析
https://blog.csdn.net/a553181867/article/details/51570854
(6)Android View 布局流程(Layout)完全解析
https://blog.csdn.net/a553181867/article/details/51524527

本文地址:https://blog.csdn.net/hfreeman2008/article/details/111882109