一.初识Window

1.窗口的定义

”窗口“的概念在Android中我们可以理解为一个独立的界面,比如说一个对话框窗口、一个菜单窗口、一个Activity界面窗口等。

Window在Android中是一个抽象类,该抽象类的唯一实现类是PhoneWindow。

View在Android中也是一个类,简单的理解成视图,比如说界面中的button、textview等。

Window对View会进行管理。

2.窗口的类型

Window的类型有很多。比如应用程序窗口、输入法窗口、Toast、Dialog等。在WindowManager中对于Window分为三类:

A.normal application windows

应用程序窗口,包括:应用启动窗口等。它的取值范围是1~99。

        /**         * Start of window types that represent normal application windows.         */        public static final int FIRST_APPLICATION_WINDOW = 1;        ......        /**         * End of types of application windows.         */        public static final int LAST_APPLICATION_WINDOW = 99;

B.sub windows

子窗口,比如:显示视频的窗口等。它的取值范围是1000~1999

        /**         * Start of types of sub-windows.  The {@link #token} of these windows         * must be set to the window they are attached to.  These types of         * windows are kept next to their attached window in Z-order, and their         * coordinate space is relative to their attached window.         */        public static final int FIRST_SUB_WINDOW = 1000;        ......        /**         * End of types of sub-windows.         */        public static final int LAST_SUB_WINDOW = 1999;

C.system-specific windows

系统特定的窗口,比如:搜索栏、通话窗口、输入法窗口、Dialog等。它的取值范围是2000~2999

        /**         * Start of system-specific window types.  These are not normally         * created by applications.         */        public static final int FIRST_SYSTEM_WINDOW     = 2000;        ......        /**         * End of types of system windows.         */        public static final int LAST_SYSTEM_WINDOW      = 2999;

ype值大小排序:system-specific windows > sub windows > normal application windows

3.窗口的布局

A.简述

窗口的布局一般分为两种:一种是平铺式布局,另一种是层叠式布局。

平铺式布局的缺点显而易见:如果需要多个窗口时,界面很难排版。层叠式布局的好处在于多个窗口层叠显示。很明显Android使用的就是层叠式布局。

一个三维坐标系:X:手机横向;Y:手机纵向;Z:手机从内往外显示。类似如下图:

WindowManagerService第一讲之WMS基础知识_第1张图片

因此不同的窗口显示是有优先级的,这里就是看窗口在Z轴上的次序(Z-ordered),很明显在Z轴上值越大越显示在上层。这个Z-ordered排序就是靠窗口的类型中的值来的。越大就越靠前显示。

这里需要引入一个类:WindowState

B.WindowState

WindowState类中记录了一个窗口应该有的所有属性,包括窗口的大小、在屏幕的层值、窗口动画过程的各种状态信息。这里我们将窗口在屏幕上层值有关联的两个变量拿出来说下。

a.mBaseLayer

mBaseLayer 主窗口层级值。该值是在WindowState对象的构造函数中进行赋值的。来看他的构造函数:

    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,            PowerManagerWrapper powerManagerWrapper) {        super(service);        .......        if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {            // The multiplier here is to reserve space for multiple            // windows in the same type layer.            mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;            mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);            ......        } else {            // The multiplier here is to reserve space for multiple            // windows in the same type layer.            mBaseLayer = mPolicy.getWindowLayerLw(this)                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;            mSubLayer = 0;            ......        }        mIsFloatingLayer = mIsImWindow || mIsWallpaper;        ......    }

这里只放涉及到的两个变量的赋值代码。可以看到mBaseLayer的赋值是通过调用getWindowLayerLw()方法来实现的:

    default int getWindowLayerLw(WindowState win) {        return getWindowLayerFromTypeLw(win.getBaseType(), win.canAddInternalSystemWindow());    }

这里获取传入的windowState的type值(即该窗口的类型值),作为传参调用getWindowLayerFromTypeLw()并返回方法结果:

    default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {            return APPLICATION_LAYER;        }        switch (type) {            case TYPE_WALLPAPER:                // wallpaper is at the bottom, though the window manager may move it.                return  1;            case TYPE_PRESENTATION:            case TYPE_PRIVATE_PRESENTATION:                return  APPLICATION_LAYER;            case TYPE_DOCK_DIVIDER:                return  APPLICATION_LAYER;            case TYPE_QS_DIALOG:                return  APPLICATION_LAYER;            case TYPE_PHONE:                return  3;            case TYPE_SEARCH_BAR:            case TYPE_VOICE_INTERACTION_STARTING:                return  4;            case TYPE_VOICE_INTERACTION:                // voice interaction layer is almost immediately above apps.                return  5;            case TYPE_INPUT_CONSUMER:                return  6;            case TYPE_SYSTEM_DIALOG:                return  7;            case TYPE_TOAST:                // toasts and the plugged-in battery thing                return  8;            case TYPE_PRIORITY_PHONE:                // SIM errors and unlock.  Not sure if this really should be in a high layer.                return  9;            case TYPE_SYSTEM_ALERT:                // like the ANR / app crashed dialogs                // Type is deprecated for non-system apps. For system apps, this type should be                // in a higher layer than TYPE_APPLICATION_OVERLAY.                return  canAddInternalSystemWindow ? 13 : 10;            case TYPE_APPLICATION_OVERLAY:                return  12;            case TYPE_DREAM:                // used for Dreams (screensavers with TYPE_DREAM windows)                return  14;            case TYPE_INPUT_METHOD:                // on-screen keyboards and other such input method user interfaces go here.                return  15;            case TYPE_INPUT_METHOD_DIALOG:                // on-screen keyboards and other such input method user interfaces go here.                return  16;            case TYPE_STATUS_BAR:                return  17;            case TYPE_STATUS_BAR_PANEL:                return  18;            case TYPE_STATUS_BAR_SUB_PANEL:                return  19;            case TYPE_KEYGUARD_DIALOG:                return  20;            case TYPE_VOLUME_OVERLAY:                // the on-screen volume indicator and controller shown when the user                // changes the device volume                return  21;            case TYPE_SYSTEM_OVERLAY:                // the on-screen volume indicator and controller shown when the user                // changes the device volume                return  canAddInternalSystemWindow ? 22 : 11;            case TYPE_NAVIGATION_BAR:                // the navigation bar, if available, shows atop most things                return  23;            case TYPE_NAVIGATION_BAR_PANEL:                // some panels (e.g. search) need to show on top of the navigation bar                return  24;            case TYPE_SCREENSHOT:                // screenshot selection layer shouldn't go above system error, but it should cover                // navigation bars at the very least.                return  25;            case TYPE_SYSTEM_ERROR:                // system-level error dialogs                return  canAddInternalSystemWindow ? 26 : 10;            case TYPE_MAGNIFICATION_OVERLAY:                // used to highlight the magnified portion of a display                return  27;            case TYPE_DISPLAY_OVERLAY:                // used to simulate secondary display devices                return  28;            case TYPE_DRAG:                // the drag layer: input for drag-and-drop is associated with this window,                // which sits above all other focusable windows                return  29;            case TYPE_ACCESSIBILITY_OVERLAY:                // overlay put by accessibility services to intercept user interaction                return  30;            case TYPE_SECURE_SYSTEM_OVERLAY:                return  31;            case TYPE_BOOT_PROGRESS:                return  32;            case TYPE_POINTER:                // the (mouse) pointer layer                return  33;            default:                Slog.e("WindowManager", "Unknown window type: " + type);                return APPLICATION_LAYER;        }    }

getWindowLayerFromTypeLw()方法根据type类型不同,返回不同的int值。

回到WindowState构造函数中,当得到这些不同int值后,乘以TYPE_LAYER_MULTIPLIER(该值在WMS中定义,为10000),再加上TYPE_LAYER_OFFSET(该值在WMS中定义,为1000)

比如窗口type是APPLICATION_LAYER,那么最终mBaseLayer的值就是2*10000+1000。

同时TYPE_LAYER_MULTIPLIER也表示类型层值倍数,即两个不同类型窗口之间最多可容纳10000个窗口;

TYPE_LAYER_OFFSET 这个值也为动画层级属性预留了1000个窗口的空间。

b.mSubLayer

mSubLayer是子窗口在屏幕上的层值。同样在WindowState的构造函数中可以看到, mSubLayer的赋值是调用getSubWindowLayerFromTypeLw()方法:

    default int getSubWindowLayerFromTypeLw(int type) {        switch (type) {            case TYPE_APPLICATION_PANEL:            case TYPE_APPLICATION_ATTACHED_DIALOG:                // 1                return APPLICATION_PANEL_SUBLAYER;            case TYPE_APPLICATION_MEDIA:                // 2                return APPLICATION_MEDIA_SUBLAYER;            case TYPE_APPLICATION_MEDIA_OVERLAY:                // -1                return APPLICATION_MEDIA_OVERLAY_SUBLAYER;            case TYPE_APPLICATION_SUB_PANEL:                // 2                return APPLICATION_SUB_PANEL_SUBLAYER;            case TYPE_APPLICATION_ABOVE_SUB_PANEL:                // 3                return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;        }        Slog.e("WindowManager", "Unknown sub-window type: " + type);        return 0;    }

根据不同子窗口的类型,返回int类型的值。如果该值为正数表示显示在上方,负数表示显示在下方。

用图来形容:

sub1、sub2的mSubLayer分别为:1、2;sub3的mSubLayer为-1

WindowManagerService第一讲之WMS基础知识_第2张图片

sub1、sub2在父窗口上方,sub3在父窗口的下方。(这三维样式有点难弄…)

当然窗口层级布局的最终实现还需要经过WMS的处理,这里先不深入分析。先大概上知晓窗口布局的显示顺序。

二.初识WindowManager

1.WindowManager的定义

WindowManager在Android中定义成一个接口类,继承ViewManager。很明显他是用来管理Window的。所以实现类是WindowManagerImpl。

对Window进行添加、删除、更新操作就会使用到WindowManager。而具体的实现是交给WMS来处理的。WindowManager和WindowManagerService之间是通过Binder来实现跨进程通信的(类似ActivityManager和AMS)

总结下:Window、WindowManager、WindowManagerService三者之间的关系如下:

WindowManagerService第一讲之WMS基础知识_第3张图片

2.WindowManager的关联类

A.ViewManager

上面的介绍中我们知道WindowManager是继承自ViewManager的,而在源码中这个类的实现很简单,仅仅定义了三种方法:分别对应view的添加、更新、移除操作。

public interface ViewManager{    /**     * Assign the passed LayoutParams to the passed View and add the view to the window.     * 

Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. *

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

这三个方法传入的参数中第一个都是View类型。说明Window是以View的形式而存在的(即我们上面有说到Window是包含View并且对View进行管理)。

WindowManager中也会继承上面的方法。而最终这些方法的实现是在WindowManagerImpl类中。这个后面会有流程介绍。

B.Window

上面有说到Window是一个抽象类,它的唯一实现类是PhoneWindow类。

C.PhoneWindow

PhoneWindow的创建时机在AMS中的Activity启动流程中有过介绍。activity launch的核心方法:ActivityThread#performLaunchActivity。其实现过程中会调用attach()方法初始化Activity。

在Activity#attach方法中:

    @UnsupportedAppUsage    final void attach(Context context, ActivityThread aThread,            Instrumentation instr, IBinder token, int ident,            Application application, Intent intent, ActivityInfo info,            CharSequence title, Activity parent, String id,            NonConfigurationInstances lastNonConfigurationInstances,            Configuration config, String referrer, IVoiceInteractor voiceInteractor,            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {        attachBaseContext(context);        mFragments.attachHost(null /*parent*/);        // 1        mWindow = new PhoneWindow(this, window, activityConfigCallback);        mWindow.setWindowControllerCallback(this);        mWindow.setCallback(this);        mWindow.setOnWindowDismissedCallback(this);        mWindow.getLayoutInflater().setPrivateFactory(this);        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {            mWindow.setSoftInputMode(info.softInputMode);        }        if (info.uiOptions != 0) {            mWindow.setUiOptions(info.uiOptions);        }        mUiThread = Thread.currentThread();        ......        // 2        mWindow.setWindowManager(                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),                mToken, mComponent.flattenToString(),                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);        if (mParent != null) {            mWindow.setContainer(mParent.getWindow());        }        mWindowManager = mWindow.getWindowManager();        mCurrentConfig = config;        mWindow.setColorMode(info.colorMode);        setAutofillOptions(application.getAutofillOptions());        setContentCaptureOptions(application.getContentCaptureOptions());    }

注释1的地方会赋值mWindow,可以看到新创建一个PhoneWindow对象。这就是PhoneWindow的创建时机。

D.WindowManagerImpl

我们接着看attach()方法中注释2的地方会调用Window#setWindowManager()方法,实现是在Window类中:

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,            boolean hardwareAccelerated) {        mAppToken = appToken;        mAppName = appName;        mHardwareAccelerated = hardwareAccelerated;        if (wm == null) {            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);        }        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);    }

可以看到如果传参wm(WindowManager类型)为空,则会调用getSystemService()方法。

来看ContextImpl中getSystemService()方法的实现:

    @Override    public Object getSystemService(String name) {        return SystemServiceRegistry.getSystemService(this, name);    }

返回值是SystemServiceRegistry中调用getSystemService()后的返回值:

    public static Object getSystemService(ContextImpl ctx, String name) {        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);        return fetcher != null ? fetcher.getService(ctx) : null;    }

SYSTEM_SERVICE_FETCHERS是一个ArrayMap类型的数据。而往这个数据中put对象是要调用registerService()方法:

    private static <T> void registerService(String serviceName, Class<T> serviceClass,            ServiceFetcher<T> serviceFetcher) {        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);    }

回到上面的getSystemService()方法中,fetcher对象的获取需要调用get()方法,对应的name就是Window中传入的WINDOW_SERVICE,在SystemServiceRegistry的静态代码块中会调用多个registerService()方法,而传入的serviceName参数就有WINDOW_SERVICE:

        registerService(Context.WINDOW_SERVICE, WindowManager.class,                new CachedServiceFetcher<WindowManager>() {            @Override            public WindowManager createService(ContextImpl ctx) {                return new WindowManagerImpl(ctx);            }});

可以看到返回的就是WindowManagerImpl实例。

最终我们回到Window#setWindowManager()方法,当WindowManager不存在的时候,我们会创建一个WindowManagerImpl实例将他强转成WindowManager类型。然后再转回WindowManagerImpl类型,并调用createLocalWindowManager()方法:

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {        return new WindowManagerImpl(mContext, parentWindow);    }

createLocalWindowManager()方法同样也是创建一个新的WIndowManagerImpl实例,不同的是创建实例传入的参数不同。这个传参有2个,包含context和window对象。

这样WindowManagerImpl实例就持有Window的引用,所以可以对Window进行相关的操作。

E.WindowManagerGlobal

我们需要对window进行增加、更新、删除动作。如上我们知道调用的实现是在WindowManagerImpl中,以addView()方法为例:

    @Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);    }

可以看到WindowManagerImpl中的addView()方法并没有具体的实现,而是调用mGlobal对象的addView()方法,mGlobal对象是WindowManagerGlobal类型的。即具体的实现是在WindowManagerGlobal中。

那WindowManagerImpl和WindowManagerGlobal是怎么联系起来的?

桥接模式!

public final class WindowManagerImpl implements WindowManager {    @UnsupportedAppUsage    // WindowManagerGlobal单例,说明一个进程中只会有一个WindowManagerGlobal实例    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();    private final Context mContext;    private final Window mParentWindow;    private IBinder mDefaultToken;    public WindowManagerImpl(Context context) {        this(context, null);    }    private WindowManagerImpl(Context context, Window parentWindow) {        mContext = context;        mParentWindow = parentWindow;    }    ...... }

总结

WindowManager的关联类之间的关系如下图:

WindowManagerService第一讲之WMS基础知识_第4张图片

三.总结

此篇主要是对WMS中涉及到基本概念有一个大致的了解。

更多相关文章

  1. Android RSA与Java RSA加密不同标准产生问题的解决方法
  2. Android sdk manager不能更新下载缓慢的解决方法
  3. android截屏代码实现方法
  4. Android获取系统的硬件信息、系统版本以及如何检测ROM类型
  5. Android Cannas.drawTextView 方法坐标问题
  6. Android开机自启动程序设置及控制方法
  7. Android随笔之——Android时间、日期相关类和方法
  8. Android NFC开发(二)——Android世界里的NFC所具备的条件以及使用
  9. Kernel.org 被黑,获取 Android 源码方法一则

随机推荐

  1. Android拍照、录像、录音代码范例
  2. android 创建简单通用工程
  3. Android之Bitmap之圆形头像
  4. ListView 去除黄色背景
  5. Android之音乐播放(二)
  6. android 蓝牙app代码
  7. android 常用对话框
  8. Android(安卓)点击回退按钮时弹出提示框
  9. Android断点续传实现
  10. android 常用方法集合