美文网首页
Android 10.0 WindowManager分析

Android 10.0 WindowManager分析

作者: 竖起大拇指 | 来源:发表于2020-10-22 16:48 被阅读0次

    Window,WindowManager和WMS

    • Window是一个抽象类,具体的实现类是PhoneWindow,它是对view进行管理的。

    • WindowManager是一个接口类,继承自父接口ViewManager,它是用来管理Window的,它的具体实现类为WindowManagerImpl。

    -WMS是WindowManager进行窗口管理的具体实施者,如果我们想要对Window进行添加和删除就可以使用WindowManager,具体的工作都是由WMS来处理的,WindowManager和WMS通过Binder来进行跨进程通信,WMS作为系统服务有多API是不会暴露给WindowManager的,这一点与ActivityManager和AMS的关系有些类似。

    Window包含了View并对View进行管理,Window是一个抽象概念,并不是真实存在,Window的实体其实也是View。WindowManager用来管理Window,而WindowManager所提供的功能最终会由WMS来处理。

    WindowManager结构

    WindowManager是一个接口类,继承自接口ViewManager,ViewManager定义了三个方法,分别用来添加,更新和删除View;

    public interface WindowManager extends 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);
    }
    

    WindowManager除了继承这些方法之外,同时也加入了很多功能,包括Window的类型和层级相关的常量,内部类以及一些方法。

    public Display getDefaultDisplay();
    
     /**
         * Special variation of {@link #removeView} that immediately invokes
         * the given view hierarchy's {@link View#onDetachedFromWindow()
         * View.onDetachedFromWindow()} methods before returning.  This is not
         * for normal applications; using it correctly requires great care.
         *
         * @param view The view to be removed.
         */
        public void removeViewImmediate(View view);
    
    

    可以看到这些方法传入的参数都是View,说明WindowManager具体管理的是以View形式存在的Window。
    WindowManager除了增加方法外,还定义了一个静态内部类LayoutParams,这就是Window的属性,了解Window的属性能更好的理解WMS的内部原理,下面我们来看看Window的属性。

    Window的属性

    Window的属性有很多种,与应用开发最密切的有三种,它们分别是Type(Window的类型),Flag(Window的标志)和SoftInputMode(软键盘相关模式),下面分别介绍这三种Window的属性。

    Window的类型

    Window的类型有很多种,比如应用程序窗口,系统错误窗口,输入法窗口,PopupWindow,Toast,Dialog等等。总的来说分为三大类:ApplicationWindow(应用程序窗口),Sub Window(子窗口),SystemWindow(系统窗口),每个大类又包含了很多种类型,它们都定义在WindowManager的静态内部类LayoutParams中。

    (1) 应用程序窗口

    Activity就是一个典型的应用程序窗口,应用程序窗口包含的类型:

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

    应用程序窗口的初始值是1,结束值是99,也就是说应用程序窗口的Type值范围为1到99,这个数值的大小涉及到窗口的层级,一般情况下,Type值越大则Z-Order排序越靠前,就越靠近用户,在屏幕越上层。

    (2) 子窗口

    子窗口,顾名思义,它不能独立存在,需要附在其他窗口才可以,PopWindow就属于子窗口,子窗口的类型定义如下:

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

    子窗口的Type值范围为1000到1999。

    (3)系统窗口

    Toast,输入法窗口,系统音量条窗口,系统错误窗口等都属于系统窗口。系统窗口的类型定义如下:

     /**
             * 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.
             * @deprecated for non-system apps. Use {@link #TYPE_APPLICATION_OVERLAY} instead.
             */
            @Deprecated
            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.
             * @deprecated for non-system apps. Use {@link #TYPE_APPLICATION_OVERLAY} instead.
             */
              //系统ALERT窗口
            @Deprecated
            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.
             * @deprecated for non-system apps. Use {@link #TYPE_APPLICATION_OVERLAY} instead.
             */
                //Toast窗口
            @Deprecated
            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.
             * @deprecated for non-system apps. Use {@link #TYPE_APPLICATION_OVERLAY} instead.
             */
            @Deprecated
            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.
             * @deprecated for non-system apps. Use {@link #TYPE_APPLICATION_OVERLAY} instead.
             */
            @Deprecated
            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;
    
    

    系统窗口的Type值范围为2000到2999。

    Window的标志

    Window的标志也就是Flag,用于控制Window的显示,同样被定义在WindowManager的内部类LayoutParams中,一共有20多个,这里我们例出几个比较常用的。

    // 只要窗口可见,就允许在开启状态的屏幕上锁屏. 
    public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
    //窗口不能获得输入焦点,设置该标志的同时,FLAG_NOT_TOUCH_MODAL也会被设置
    public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
    //窗口不接收任何触摸事件
    public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
    //在该窗口区域外的触摸事件传递给其他的Window,而自己只会处理窗口区域内的触摸事件
    public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
    //只要窗口可见,屏幕就会一直亮着
    public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;
    //允许窗口超过屏幕之外
    public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
    //隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示
    public static final int FLAG_FULLSCREEN      = 0x00000400;
    //窗口可以在锁屏的窗口之上显示
    public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
    //当用户的脸贴近屏幕时(比如打电话),不会去响应此事件
    public static final int FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000;
    //窗口显示时将屏幕点亮
    public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
    
    

    设置Window的Flag有三种方法,第一种方法通过Window的addFlags方法:

    Window mWindow =getWindow(); 
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
    

    其内部实现还是调用了setFlags方法。

    第二种通过Window的setFlags方法.

    Window mWindow =getWindow();            
    mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
    

    第三种则是给LayoutParams设置Flag,并通过WindowManager的addView方法进行添加:

    WindowManager.LayoutParams mWindowLayoutParams = new WindowManager.LayoutParams();
    mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
    WindowManager mWindowManager =(WindowManager)getSystemService(Context.WINDOW_SERVICE);  
    TextView mTextView=new TextView(this);
    mWindowManager.addView(mTextView, mWindowLayoutParams);
    

    软键盘相关模式

    窗口和窗口的叠加是非常常见的场景,但如果其中的窗口时软键盘窗口,可能就会出现一些问题,比如典型的用户登录界面,默认的情况下弹出软键盘窗口可能会盖住输入框,这样用户的体验会非常糟糕。

    为了使得软键盘窗口能够按照期望来显示,WindowManager的静态内部类LayoutParams中定义了软键盘相关模式,这里给出几个常用的:

    //没有指定状态,系统会选择一个合适的状态或依赖于主题的设置
    public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
    //不会改变软键盘状态
    public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
    //当用户进入该窗口时,软键盘默认隐藏
    public static final int SOFT_INPUT_STATE_HIDDEN = 2;
    //当窗口获取焦点时,软键盘总是被隐藏
    public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;
    //当软键盘弹出时,窗口会调整大小
    public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
    //当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的
    public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
    

    上面给出的SoftInputMode与AndroidManifest中Activity的属性android:windowSoftInputMode是对应的。另外,除了可以在AndroidMainfest中设置外,还可以在Java代码中为Window设置SoftInputMode。

    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    

    Window的添加过程

    WindowManager对Window的操作最终都会交由WMS来仅从处理,窗口的操作分为两大部分,一部分是WindowManager处理部分,另外一部分是WMS处理部分。我们知道Window分为三大类,分别是Applicaiton Window,Sub Window 和 System Window, 对于不同类型的窗口添加过程会有所不同,但是对于WMS处理部分,过程基本上都是一样的。

    无论是哪种窗口,它的添加过程在WMS处理部分中基本上是类似的,只不过会在权限和窗口显示次序等方面会有所不同。我们已应用程序窗口Activity为例来说明:

     @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*/);
    
            mWindow = new PhoneWindow(this, window, activityConfigCallback); // 1
            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();
    
            mMainThread = aThread;
            mInstrumentation = instr;
            mToken = token;
            mAssistToken = assistToken;
            mIdent = ident;
            mApplication = application;
            mIntent = intent;
            mReferrer = referrer;
            mComponent = intent.getComponent();
            mActivityInfo = info;
            mTitle = title;
            mParent = parent;
            mEmbeddedID = id;
            mLastNonConfigurationInstances = lastNonConfigurationInstances;
            if (voiceInteractor != null) {
                if (lastNonConfigurationInstances != null) {
                    mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
                } else {
                    mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                            Looper.myLooper());
                }
            }
            
            // 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处创建了PhoneWindow。
    注释2处调用了PhoneWindow的setWindowManager方法,这个方法的具体实现在PhoneWindow的父类Window中。

    Window.java#setWindowManager()
     /**
         * Set the window manager for use by this Window to, for example,
         * display panels.  This is <em>not</em> used for displaying the
         * Window itself -- that must be done by the client.
         *
         * @param wm The window manager for adding new windows.
         */
        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);  //  1
            }
            mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); // 2
        }
    

    注释1处如果传入的WindowManager为null,就会调用Context的getSystemService方法,并传入服务的名称Context.WINDOW_SERVICE("window"),Context的实现类是ContextImpl.

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

    createLocalWindowManager方法同样也是创建WindowManagerImpl,不同的是这次创建WindowManagerImpl时将创建它的Window作为参数传了进来,这样WindowManagerImpl就持有了Window的引用,就可以对Window进行操作。比如,在Window中添加View,来看下WindowManagerImpl的addView方法:

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

    注释1处调用了WindowManagerGlobal的addView方法,其中最后一个参数mParentWindow就是Window,可以看出WindowManagerImpl虽然时WindowManager的实现类,但是却没有实现什么功能,而是将功能委托给WindowManagerGlobal。
    来查看WindowManagerImpl中如何定义的WindowManagerGlobal:

    public final class WindowManagerImpl implements WindowManager {
        @UnsupportedAppUsage
        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;
        }
    
        public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
            return new WindowManagerImpl(mContext, parentWindow);
        }
    
        public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
            return new WindowManagerImpl(displayContext, mParentWindow);
        }
    
        /**
         * Sets the window token to assign when none is specified by the client or
         * available from the parent window.
         *
         * @param token The default token to assign.
         */
        public void setDefaultToken(IBinder token) {
            mDefaultToken = token;
        }
    
        @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
        .................
    }
    

    可以看出WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal实列。
    在一个进程中WindowManagerImpl可能会有多个实例。

    WindowManagerGlobal.java#addView()
    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");
            }
    
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {
                parentWindow.adjustLayoutParamsForSubWindow(wparams);  //1
            } 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) {
                // Start watching for system property changes.
                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);
                }
    
                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.
                }
    
                // If this is a panel window, then find the window it is being
                // attached to for future reference.
                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);
                        }
                    }
                }
    
                root = new ViewRootImpl(view.getContext(), display); // 2
    
                view.setLayoutParams(wparams);
    
                mViews.add(view);
                mRoots.add(root); // 3
                mParams.add(wparams);
    
                // do this last because it fires off messages to start doing things
                try {
                    root.setView(view, wparams, panelParentView); // 4
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                }
            }
        }
    

    首先会对参数View,params和display进行检查。
    注释1:如果当前窗口要作为子窗口,就会根据父窗口对子窗口WindowManager.LayoutParams类型的wparams对象进行相应的调整。
    注释2处创建了ViewRootImp并赋值给root。
    注释3处将root存入到ArrayList<ViewRootImpl>类型的mRoot中,除了mRoots,mViews和mParams也是ArrayList类型,分别用于存储窗口的view对象和WindowManager.LayoutParams类型的wparams对象。
    注释4处调用了ViewRootImpl的setView方法

    ViewRootImpl身负了很多职责:

    View树的根并管理View树
    触发View的测量、布局和绘制
    输入事件的中转站
    管理Surface
    负责与WMS进行进程间通信

    ViewRootImpl.java#setView()
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
               ...
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        //1
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } 
                    ...
        }
    

    注释1处mWindowSession是IWindowSession对象。在创建ViewRootImpl对象时被实例化。

     public ViewRootImpl(Context context, Display display) {
            mContext = context;
            mWindowSession = WindowManagerGlobal.getWindowSession();
            mDisplay = display;
        .....................
    }
    
    WindowManagerGlobal#.java#getWindowSession()
     @UnsupportedAppUsage
        public static IWindowSession getWindowSession() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        // Emulate the legacy behavior.  The global instance of InputMethodManager
                        // was instantiated here.
                        // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                        InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                        IWindowManager windowManager = getWindowManagerService();  //1
                        sWindowSession = windowManager.openSession( //2
                                new IWindowSessionCallback.Stub() {
                                    @Override
                                    public void onAnimatorScaleChanged(float scale) {
                                        ValueAnimator.setDurationScale(scale);
                                    }
                                });
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowSession;
            }
        }
    

    注释1这里getWindowManagerService()通过AIDL返回WindowManagerService实例。
    注释2调用WindowManagerService#openSession()。

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

    WindowManagerService.java#openSession()

     @Override
        public IWindowSession openSession(IWindowSessionCallback callback) {
            return new Session(this, callback);
        }
    
    

    返回一个Session对象。也就是说在ViewRootImpl.setView()中调用的时Session.addToDisplay().

    继续回到ViewRootImpl#setView()方法,总结一下:
    注释1主要就是调用了mWindowSession的addToDisplay方法。
    mWindowSession是IWindowSession类型的,它是一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,它的Server端的实现为Session,此前包含ViewRootImpl在内的代码逻辑都是运行在本地进程的,而Session的addToDisplay方法则运行在WMS所在的进程。

    Session.java
    class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    
    final WindowManagerService mService;
    ...................
    
      public Session(WindowManagerService service, IWindowSessionCallback callback) {
            mService = service;
    
          ....................
    }
    
    
    
     @Override
        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来保存这些Session。这样剩下的工作就交给WMS来处理,在WMS中会为这个添加的窗口分配Surface,并确定窗口显示次序,可见负责显示界面的是画布Surface,而不是窗口本身。WMS会将它所管理的Surface交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上。

    相关文章

      网友评论

          本文标题:Android 10.0 WindowManager分析

          本文链接:https://www.haomeiwen.com/subject/fmnumktx.html