源码阅读分析 Window底层原理与系统架构

作了一段的时间的 android 咱们就开始听到有人说 AMS、WMS、Window、WindowManager、WindowManagerService等等这些词汇,可能了解可是脑海里未必有架构图, 此次咱们就从源码的角度来了解一下。在阅读本文以前但愿你能够花点时间了解下面几篇文章:java

1. 插件式换肤框架搭建 - setContentView源码阅读 2. Android进程间的通讯 - IPC(机制)Binder的原理和源码阅读 2. Android插件化架构 - Activity的启动流程分析 3. 源码解析 - View的绘制流程android

####如何阅读源码 上面几篇文章和我今天的这篇文章有着千丝万缕的联系,我为何能够把他们分开来?有很重要的一点,咱们是须要用到才会去看源码,打个比方我想作一个皮肤切换的功能,那么我确定须要去了解布局资源的加载过程,而这个你仅仅从网上看看别人写好的文章或者 copy 几行代码相信你应该很难作到,固然去 github 上面选个 demo 用用也行,但你内心不以为少了点什么吗?还有咱们最好可以点到即止,带着疑问,只须要清楚咱们想要了解什么,今天的不少源码或多或少都会涉及到上面几篇文章的知识点但咱们不用管。相信我,只要你可以多对着别人的文章本身打开源码看看,随着时间的推移当咱们再看系统源码的时候就会驾轻就熟,解决问题不再是靠蒙和试**(若是你在开发过程当中有时候靠蒙能够在文章末尾刷个赞),并且若是心中有整个 android 应用层的源码架构图,对于开发和解决问题方面屡试不爽(若是你能看懂 native 层的源码更好)**。git

####咱们的疑问 咱们只是知道 setContentView 方法能够设置显示咱们的布局,这篇 插件式换肤框架搭建 - setContentView源码阅读 文章只是带你们了解了布局的层次结构,但你了解 Window 和 WindowManager 都干了些什么吗?又或者当咱们触摸一个 EditText 的时候会弹出一个系统的键盘,为何弹出键盘咱们的 Activity 布局会自动作调整?咱们弹一个 Toast 但就算咱们退出 Activity 或是整个应用 Toast 仍是会存在?github

PhoneWindow的建立过程

咱们以 Activity 的 setContentView 做为入口能够看到这么两行代码:windows

/**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * @param layoutResID Resource ID to be inflated.
     *
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
复制代码

getWindow().setContentView(layoutResID) 这行代码只是去解析咱们的布局,没干其余任何事情,这行代码我就再也不分析源码了,今天的重点不在这里若是想了解请看这篇插件式换肤框架搭建 - setContentView源码阅读,getWindow() 返回的是 mWindow 而 mWindow 是在 attach 方法中实例化的:设计模式

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) {
        // 建立一个 PhoneWindow 实例
        mWindow = new PhoneWindow(this, window);
        // 设置一个 ControllerCallback
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        // 这个你看方法名就知道是什么意思了
        mWindow.setOnWindowDismissedCallback(this);
        // 给 Window 设置一个 WindowManager 
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        // Activity 本身也有一个 WindowManager
        mWindowManager = mWindow.getWindowManager();
    }
复制代码

那么 attach 方法究竟是在何时调用的呢?在 ActivityThread 中的 performLaunchActivity 方法中调用的,这里涉及到 Activity 的启动流程分析想了解请看这篇Android插件化架构 - Activity的启动流程分析bash

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        Activity activity = null;
        try {
            // 利用反射建立 Activity 实例对象
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            if (activity != null) {
                // 咱们要找的方法在这里
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);

                // 设置主题
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
            }
        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }
        // 返回 Activity 实例
        return activity;
    }

复制代码

目前咱们只知道了经过 setContentView 方法会调用 mWindow 的 setContentView 方法,这个方法只是去解析咱们的布局而已什么时都没作,而 mWindow 的实例实在 activity 的 attach 方法中调用的,而这个方法是由 ActivityThread 调用的而后就没有了,那么布局究竟是怎么显示的?markdown

布局的测量和绘制过程

Android插件化架构 - Activity的启动流程分析中咱们可以在 ActivityThread 中找到 handleResumeActivity 这个方法:网络

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        // 先执行 Activity 的 onResume 生命周期方法
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);
       
        if (r != null) {
            final Activity a = r.activity;
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                // 获取 Activity 也就是 PhoneWindow 的根布局
                View decor = r.window.getDecorView();
                // 设置为显示
                decor.setVisibility(View.INVISIBLE);
                // 获取 Activity 的 WindowManager 这个对象在上面的 Activity 的 attach 方法中赋值的
                ViewManager wm = a.getWindowManager();
                // 获取PhoneWindow的 WindowManager.LayoutParams 
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                // 指定 type ,这是一个比较重要的概念,待会下面会介绍到 指定窗口的类型
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 调用 ViewManager 的 addView 方法
                    wm.addView(decor, l);
                }
            }
        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }
复制代码

最终调用了 ViewManager 的 addView 方法,可是咱们发现 ViewManager 实际上是个接口,因此咱们得去找实现方法,是在上面 Activity 的 attach 方法经过 context.getSystemService(Context.WINDOW_SERVICE) 获取的,找到 context 的实例类 ContextImpl 的 getSystemService 方法其实调用了 SystemServiceRegistry.getSystemService 方法:session

/**
    * Gets a system service from a given context.
    */
    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 是一个静态的 HashMap 对象,是经过静态代码块赋值的。这里其实咱们能够总结一下,经过 Context 获取的系统服务其实早就被注册和添加到 SYSTEM_SERVICE_FETCHERS 集合中了,这是典型的单例设计模式。至此咱们总算找到了这个类 WindowManagerImpl 的 addView 方法。

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

WindowManagerImpl 是 ViewManager 的实现类却把活交给了 WindowManagerGlobal 方法,并且咱们发现 mGlobal 对象尽然是个单例,为何要这么设计在文章中我就不作过多的讲解了。咱们看下 mGlobal 的 addView 方法:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

        // 一些参数的校验
        // ......
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            // 若是是子 View 须要调整一些布局的参数
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // 开始观看系统属性的变化。
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
            // 经过View 去集合中查找位置,第一次添加确定是 0
            int index = findViewLocked(view, false);
            if (index >= 0) {
                // 若是添加过的状况,判断是否是正在销毁中
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { // 若是不是正在销毁,又添加过那么抛异常结束运行 throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } // 若是是一个子窗口,咱们去找到它的父窗口的 View // 窗口的类型,下面会介绍到 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } } // 构建ViewRootImpl,暂时无论ViewRootImpl是什么东西 root = new ViewRootImpl(view.getContext(), display); // 给 View 设置 LayoutParams view.setLayoutParams(wparams); // 下面是一些保存工做添加到集合,一一对应起来 mViews.add(view); mRoots.add(root); mParams.add(wparams); } // 最后一行代码,用来触发作事 // 将View显示到手机上。setView方法比较复杂,走了这么就最重要的方法在这里面。 try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } } 复制代码

走了这么久最重要的方法其实就是 root.setView(view, wparams, panelParentView); 这行代码,很复杂恰恰这个方法又是最主要的,但愿我讲得并不太深刻并且通俗易懂。在分析这个以前咱们先讲一下上面反复出现的 type 属性。

Window的三种类型

Window(窗口)是有类型的,并且上面咱们也看到了不一样的 type 会作不一样的处理,Window 分为三种类型:系统 Window,应用程序 Window,子 Window

  • 常见的系统Window,好比在手机电量低的时候,会有一个提示电量低的Window,咱们输入文字的时候,会弹出输入法Window,还有搜索条Window,来电显示Window,Toast对应的Window,能够总结出来,系统Window是独立与咱们的应用程序的,对于应用程序而言,咱们理论上是没法建立系统Window,由于没有权限,这个权限只有系统进程有。所对应的层级区间是 2000 以上

  • 应用程序Window,好比 Activity 就是一个应用程序 Window 从上面源码能够看到 type 给的是 TYPE_BASE_APPLICATION 。所对应的层级区间是 1 - 99

  • 子Window,所谓的子Window,是说这个Window必需要有一个父窗体,好比PopWindow,Dialog 等等 。所对应的层级区间是 1000 - 1999

那么每一个层级具体有那些,请看 WindowManager.LayoutParams中的 type 值,这里我贴出来可是不作翻译,由于我英语很差:

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

        /**
         * Window type: an application window that serves as the "base" window
         * of the overall application; all other application windows will
         * appear on top of it.
         * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_BASE_APPLICATION = 1; /** * Window type: a normal application window. The {@link #token} must be * an Activity token identifying who the window belongs to. * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_APPLICATION        = 2;

        /**
         * Window type: special application window that is displayed while the
         * application is starting.  Not for use by applications themselves;
         * this is used by the system to display something until the
         * application can show its own windows.
         * In multiuser systems shows on all users' windows. */ public static final int TYPE_APPLICATION_STARTING = 3; /** * Window type: a variation on TYPE_APPLICATION that ensures the window * manager will wait for this window to be drawn before the app is shown. * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_DRAWN_APPLICATION = 4;

        /**
         * End of types of application windows.
         */
        public static final int LAST_APPLICATION_WINDOW = 99;

        /**
         * 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;

        /**
         * Window type: a panel on top of an application window.  These windows
         * appear on top of their attached window.
         */
        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

        /**
         * Window type: window for showing media (such as video).  These windows
         * are displayed behind their attached window.
         */
        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

        /**
         * Window type: a sub-panel on top of an application window.  These
         * windows are displayed on top their attached window and any
         * {@link #TYPE_APPLICATION_PANEL} panels.
         */
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

        /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout
         * of the window happens as that of a top-level window, <em>not</em>
         * as a child of its container.
         */
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

        /**
         * Window type: window for showing overlays on top of media windows.
         * These windows are displayed between TYPE_APPLICATION_MEDIA and the
         * application window.  They should be translucent to be useful.  This
         * is a big ugly hack so:
         * @hide
         */
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

        /**
         * Window type: a above sub-panel on top of an application window and it's * sub-panel windows. These windows are displayed on top of their attached window * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels. * @hide */ public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; /** * End of types of sub-windows. */ public static final int LAST_SUB_WINDOW = 1999; /** * Start of system-specific window types. These are not normally * created by applications. */ public static final int FIRST_SYSTEM_WINDOW = 2000; /** * Window type: the status bar. There can be only one status bar * window; it is placed at the top of the screen, and all other * windows are shifted down so they are below it. * In multiuser systems shows on all users' windows.
         */
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;

        /**
         * Window type: the search bar.  There can be only one search bar
         * window; it is placed at the top of the screen.
         * In multiuser systems shows on all users' windows. */ public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; /** * Window type: phone. These are non-application windows providing * user interaction with the phone (in particular incoming calls). * These windows are normally placed above all applications, but behind * the status bar. * In multiuser systems shows on all users' windows.
         */
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;

        /**
         * Window type: system window, such as low power alert. These windows
         * are always on top of application windows.
         * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; /** * Window type: keyguard window. * In multiuser systems shows on all users' windows.
         * @removed
         */
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;

        /**
         * Window type: transient notifications.
         * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; /** * Window type: system overlay windows, which need to be displayed * on top of everything else. These windows must not take input * focus, or they will interfere with the keyguard. * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;

        /**
         * Window type: priority phone UI, which needs to be displayed even if
         * the keyguard is active.  These windows must not take input
         * focus, or they will interfere with the keyguard.
         * In multiuser systems shows on all users' windows. */ public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; /** * Window type: panel that slides out from the status bar * In multiuser systems shows on all users' windows.
         */
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;

        /**
         * Window type: dialogs that the keyguard shows
         * In multiuser systems shows on all users' windows. */ public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9; /** * Window type: internal system error windows, appear on top of * everything they can. * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;

        /**
         * Window type: internal input methods windows, which appear above
         * the normal UI.  Application windows may be resized or panned to keep
         * the input focus visible while this window is displayed.
         * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11; /** * Window type: internal input methods dialog windows, which appear above * the current input method window. * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;

        /**
         * Window type: wallpaper window, placed behind any window that wants
         * to sit on top of the wallpaper.
         * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13; /** * Window type: panel that slides out from over the status bar * In multiuser systems shows on all users' windows.
         */
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;

        /**
         * Window type: secure system overlay windows, which need to be displayed
         * on top of everything else.  These windows must not take input
         * focus, or they will interfere with the keyguard.
         *
         * This is exactly like {@link #TYPE_SYSTEM_OVERLAY} except that only the
         * system itself is allowed to create these overlays.  Applications cannot
         * obtain permission to create secure system overlays.
         *
         * In multiuser systems shows only on the owning user's window. * @hide */ public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15; /** * Window type: the drag-and-drop pseudowindow. There is only one * drag layer (at most), and it is placed on top of all other windows. * In multiuser systems shows only on the owning user's window.
         * @hide
         */
        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;

        /**
         * Window type: panel that slides out from under the status bar
         * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17; /** * Window type: (mouse) pointer * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;

        /**
         * Window type: Navigation bar (when distinct from status bar)
         * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19; /** * Window type: The volume level overlay/dialog shown when the user * changes the system volume. * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;

        /**
         * Window type: The boot progress dialog, goes on top of everything
         * in the world.
         * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21; /** * Window type to consume input events when the systemUI bars are hidden. * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;

        /**
         * Window type: Dreams (screen saver) window, just above keyguard.
         * In multiuser systems shows only on the owning user's window. * @hide */ public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23; /** * Window type: Navigation bar panel (when navigation bar is distinct from status bar) * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;

        /**
         * Window type: Display overlay window.  Used to simulate secondary display devices.
         * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26; /** * Window type: Magnification overlay window. Used to highlight the magnified * portion of a display when accessibility magnification is enabled. * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;

        /**
         * Window type: keyguard scrim window. Shows if keyguard needs to be restarted.
         * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_KEYGUARD_SCRIM = FIRST_SYSTEM_WINDOW+29; /** * Window type: Window for Presentation on top of private * virtual display. */ public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30; /** * Window type: Windows in the voice interaction layer. * @hide */ public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31; /** * Window type: Windows that are overlaid <em>only</em> by a connected {@link * android.accessibilityservice.AccessibilityService} for interception of * user interactions without changing the windows an accessibility service * can introspect. In particular, an accessibility service can introspect * only windows that a sighted user can interact with which is they can touch * these windows or can type into these windows. For example, if there * is a full screen accessibility overlay that is touchable, the windows * below it will be introspectable by an accessibility service even though * they are covered by a touchable window. */ public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32; /** * Window type: Starting window for voice interaction layer. * @hide */ public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33; /** * Window for displaying a handle used for resizing docked stacks. This window is owned * by the system process. * @hide */ public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34; /** * Window type: like {@link #TYPE_APPLICATION_ATTACHED_DIALOG}, but used * by Quick Settings Tiles. * @hide */ public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35; /** * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is * reserved for screenshot region selection. These windows must not take input focus. * @hide */ public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36; /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; 复制代码

窗口与WindowManagerService服务的链接过程

回到 ViewRootImpl 中的 setView 方法:

/**
    * We have one child
    */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // ......
                // 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();
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                // ......
        }
    }
复制代码

requestLayout() 这个方法颇有含金量,若是想了解看下这篇 源码解析 - View的绘制流程 ,咱们主要仍是分析mWindowSession.addToDisplay 方法,mWindowSession 又是什么呢?看到 Session 其实仍是知道什么意思,是否是真的和网络的 Session 差很少呢?mWindowSession 是经过 WindowManagerGlobal.getWindowSession(); 获取的,咱们去看下:

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }
复制代码

这又是一个典型的 IPC 的通讯机制,和 AMS 很是的相似想了解看下这篇 Android进程间的通讯 - IPC(机制)Binder的原理和源码阅读 ,如今咱们去服务端 WindowManagerService 的 openSession 方法:

@Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }
复制代码

Session咱们总算是获取到了,服务端 WindowManagerService 建立了一个 Session 对象返回回来了,接下来咱们发现 Session 的 addToDisplay 方法来到了 WindowManager 的 addWindow 方法,相信应该差很少快完了吧,这不是人干的事:

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        ......  
  
        WindowState attachedWindow = null;  
        final int type = attrs.type;
  
        synchronized(mWindowMap) {  
            ......  
  
            if (mWindowMap.containsKey(client.asBinder())) {  
                ......  
                return WindowManagerImpl.ADD_DUPLICATE_ADD;  
            } 
复制代码

这段代码首先在WindowManagerService类的成员变量mWindowMap所描述的一个HashMap中检查是否存在一个与参数client所对应的WindowState对象,若是已经存在,那么就说明WindowManagerService服务已经为它建立过一个WindowState对象了,所以,这里就不会继续往前执行,而是直接返回一个错误码WindowManagerImpl.ADD_DUPLICATE_ADD。咱们继续往前看代码:

if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {  
    attachedWindow = windowForClientLocked(null, attrs.token, false);  
    if (attachedWindow == null) {  
        ......  
        return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;  
    }  
    if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW  
            && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {  
        ......  
        return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;  
    }  
}  
复制代码

参数attrs指向的是一个WindowManager.LayoutParams对象,用来描述正在启动的Activity组件的UI布局,当它的成员变量type的值大于等于FIRST_SUB_WINDOW而且小于等于LAST_SUB_WINDOW的时候,就说明如今要增长的是一个子窗口。在这种状况下,就必需要指定一个父窗口,而这个父窗口是经过数attrs指向的是一个WindowManager.LayoutParams对象的成员变量token来指定的,所以,这段代码就会调用WindowManagerService类的另一个成员函数windowForClientLocked来得到用来描述这个父窗口的一个WindowState对象,而且保存在变量attachedWindow。

若是获得变量attachedWindow的值等于null,那么就说明父窗口不存在,这是不容许的,所以,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。另外一方面,若是变量attachedWindow的值不等于null,可是它的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量type的值也是大于等于FIRST_SUB_WINDOW而且小于等于LAST_SUB_WINDOW,那么也说明找到的父窗口也是一个子窗口,这种状况也是不容许的,所以,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。

继续看代码:

// 一些检查合法的代码

win = new WindowState(session, client, token,  
        attachedWindow, attrs, viewVisibility);  
......  
mPolicy.adjustWindowParamsLw(win.mAttrs);  
  
res = mPolicy.prepareAddWindowLw(win, attrs);  
if (res != WindowManagerImpl.ADD_OKAY) {  
    return res;  
} 
复制代码

经过上面的合法性检查以后,这里就能够为正在增长的窗口建立一个WindowState对象了。 WindowManagerService类的成员变量mPolicy指向的是一个实现了WindowManagerPolicy接口的窗口管理策略器。在Phone平台中,这个窗口管理策略器是由com.android.internal.policy.impl.PhoneWindowManager来实现的,它负责对系统中的窗口实现制定一些规则。这里主要是调用窗口管理策略器的成员函数adjustWindowParamsLw来调整当前正在增长的窗口的布局参数,以及调用成员函数prepareAddWindowLw来检查当前应用程序进程请求增长的窗口是不是合法的。若是不是合法的,即变量res的值不等于WindowManagerImpl.ADD_OKAY,那么函数就不会继续向前执行,而直接返回错误码res。

咱们就先看到这里了,你甚至还能够在去了解 WindowState 的成员变量都有一些啥做用,又或者是那些合法的检查代码都有什么用等等,最后我再画一张草图:

Activity组件与WindowManagerService服务之间的链接模型.png
我花了大概半个多月的时间才勉强看懂一些源码,固然包括这篇文章中的全部源码连接,若是看不太懂须要多花些时间,若是文字让你受不了能够看看个人直播视频,同时这也是自定义View部分的最后一篇文章了。

全部分享大纲:Android进阶之旅 - 自定义View篇

视频讲解地址:http://pan.baidu.com/s/1pLNXkxl

相关文章
相关标签/搜索