Android 10.0 WindowManager分析

Window,WindowManager和WMS

  • Window是一個抽象類,具體的實現類是PhoneWindow,它是對view進行管理的。

  • WindowManager是一個接口類,繼承自父接口ViewManager,它是用來管理Window的,它的具體實現類為WindowManagerImpl。

-WMS是WindowManager進行窗口管理的具體實施者,如果我們想要對Window進行添加和刪除就可以使用WindowManager,具體的工作都是由WMS來處理的,WindowManager和WMS通過Binder來進行跨進程通信,WMS作為系統(tǒng)服務有多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除了增加方法外,還定義了一個靜態(tài)內部類LayoutParams,這就是Window的屬性,了解Window的屬性能更好的理解WMS的內部原理,下面我們來看看Window的屬性。

Window的屬性

Window的屬性有很多種,與應用開發(fā)最密切的有三種,它們分別是Type(Window的類型),Flag(Window的標志)和SoftInputMode(軟鍵盤相關模式),下面分別介紹這三種Window的屬性。

Window的類型

Window的類型有很多種,比如應用程序窗口,系統(tǒng)錯誤窗口,輸入法窗口,PopupWindow,Toast,Dialog等等??偟膩碚f分為三大類:ApplicationWindow(應用程序窗口),Sub Window(子窗口),SystemWindow(系統(tǒng)窗口),每個大類又包含了很多種類型,它們都定義在WindowManager的靜態(tài)內部類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.
         */
        //應用程序啟動窗口類型,用于系統(tǒng)在應用程序窗口啟動前顯示的窗口
        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)系統(tǒng)窗口

Toast,輸入法窗口,系統(tǒng)音量條窗口,系統(tǒng)錯誤窗口等都屬于系統(tǒng)窗口。系統(tǒng)窗口的類型定義如下:

 /**
         * Start of system-specific window types.  These are not normally
         * created by applications.
         */
          //系統(tǒng)窗口類型初始值
        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.
         */
        //系統(tǒng)狀態(tài)欄窗口
        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.
         */
          //系統(tǒng)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;

系統(tǒng)窗口的Type值范圍為2000到2999。

Window的標志

Window的標志也就是Flag,用于控制Window的顯示,同樣被定義在WindowManager的內部類LayoutParams中,一共有20多個,這里我們例出幾個比較常用的。

// 只要窗口可見,就允許在開啟狀態(tài)的屏幕上鎖屏. 
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;
//在該窗口區(qū)域外的觸摸事件傳遞給其他的Window,而自己只會處理窗口區(qū)域內的觸摸事件
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的靜態(tài)內部類LayoutParams中定義了軟鍵盤相關模式,這里給出幾個常用的:

//沒有指定狀態(tài),系統(tǒng)會選擇一個合適的狀態(tài)或依賴于主題的設置
public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
//不會改變軟鍵盤狀態(tài)
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處創(chuàng)建了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方法同樣也是創(chuàng)建WindowManagerImpl,不同的是這次創(chuàng)建WindowManagerImpl時將創(chuàng)建它的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處創(chuàng)建了ViewRootImp并賦值給root。
注釋3處將root存入到ArrayList<ViewRootImpl>類型的mRoot中,除了mRoots,mViews和mParams也是ArrayList類型,分別用于存儲窗口的view對象和WindowManager.LayoutParams類型的wparams對象。
注釋4處調用了ViewRootImpl的setView方法

ViewRootImpl身負了很多職責:

View樹的根并管理View樹
觸發(fā)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對象。在創(chuàng)建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().

繼續(xù)回到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混合并繪制到屏幕上。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容