1. 程式人生 > >Android6.0 WMS(三) WMS視窗次序

Android6.0 WMS(三) WMS視窗次序

這篇部落格我們主要分析下,視窗位置排序的一些原理。

一、新增視窗的時候 調整視窗位置

上篇部落格我們分析了WMS的addWindow函式,這裡我們就視窗的次序問題繼續分析。

            boolean imMayMove = true;

            if (type == TYPE_INPUT_METHOD) {//如果視窗類是輸入法視窗
                win.mGivenInsetsPending = true;
                mInputMethodWindow = win;
                addInputMethodWindowToListLocked(win);//插入輸入法視窗到應用視窗上層
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {//如果視窗是輸入法對話方塊
                mInputMethodDialogs.add(win);
                addWindowToListInOrderLocked(win, true);//插入到正常位置
                moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));//調整對話方塊位置
                imMayMove = false;
            } else {
                addWindowToListInOrderLocked(win, true);//插入正常位置
                if (type == TYPE_WALLPAPER) {
                    mLastWallpaperTimeoutTime = 0;
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (mWallpaperTarget != null
                        && mWallpaperTarget.mLayer >= win.mBaseLayer) {
                    // If there is currently a wallpaper being shown, and
                    // the base layer of the new window is below the current
                    // layer of the target window, then adjust the wallpaper.
                    // This is to avoid a new window being placed between the
                    // wallpaper and its target.
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
            }

DisplayContent類的mWindows列表按Z序儲存了每個視窗,這段程式碼就是在根據視窗型別把視窗加入到DisplayContent合適位置。

addInputMethodWindowToListLocked方法作用就是一個輸入法視窗放子啊需要顯示輸入法視窗的上面。

addWindowToListInOrderLocked將一個視窗插入到視窗堆疊的當前位置。

我們繼續看addWindow函式,

            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;

            if (displayContent.isDefaultDisplay) {
                mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,//計算視窗大小
                        outOutsets);
            } else {
                outContentInsets.setEmpty();
                outStableInsets.setEmpty();
            }

            if (mInTouchMode) {
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;//加入支援觸屏的標誌
            }
            if (win.mAppToken == null || !win.mAppToken.clientHidden) {
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;//加入應用可用的標誌
            }

            mInputMonitor.setUpdateInputWindowsNeededLw();//設定更新輸入法視窗的標誌

            boolean focusChanged = false;
            if (win.canReceiveKeys()) {//如果視窗能接受輸入計算是否引起焦點變化
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                moveInputMethodWindowsIfNeededLocked(false);//調整輸入法的視窗位置
            }

            assignLayersLocked(displayContent.getWindowList());//重新計算z軸的位置
            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.

            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);//更新輸入法視窗的資訊

            if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client "
                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));

            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                reportNewConfig = true;//配置發生變化
            }
        }

        if (reportNewConfig) {
            sendNewConfiguration();//傳送新的配置
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }

如果視窗顯示在預設的顯示裝置,呼叫mPolicy的getInsetHintLw函式來獲得除了狀態條、導航條所佔區域後的視窗大小。

接下來如果視窗能接受輸入,呼叫updateFocusedWindowLocked來重新確定系統的焦點位置。如果焦點發生變化,則將imMayMove置為false。

新加入的視窗的位置在前面呼叫addWindowToListInOrderLocked的時候位置已經確定了,所以這裡呼叫assignLayersLocked只是重新計算Z軸的位置。如果呼叫updateOrientationFromAppTokensLocked函式計算視窗的配置發生變化,呼叫sendNewConfiguration函式傳送配置。


二、確定視窗的mLayer值

顯示裝置的水平方向,垂直方向作為X軸Y軸,我們還可以想象有一個垂直於螢幕的Z軸,Z軸的值越來越靠近螢幕。系統中所有的視窗都按次序排列在Z軸上。視窗物件WindowState的成員變數mLayer表示視窗在Z軸的值,值越小越靠近底層。

WMS作用之一就是管理各個視窗Z軸位置,確保正確顯示。在所有視窗中輸入法和桌布視窗比較特殊。輸入法窗口出現時,需要顯示在應用視窗的前面。桌布視窗通常在底層,但是又不是位於所有視窗的底層,而是位於當前Activity視窗的下面。

因此,當系統調整某個應用視窗的位置時,如果需要也會調整輸入法和桌布視窗,使當前Activity的視窗位於輸入法視窗和桌布視窗之間。

WindowState的成員變數mLayer的值表示視窗在Z軸的位置,這個值越小,位置越靠下。mLayer是通過計算得到,會經常變化。WindowState的另一個成員變數mBaseLayer的值是固定不變的,只和視窗型別有關。mLayer的值是根據mBaseLayer的值計算而來。

下面我們先來看看mBaseLayer的值如何而來,在WindowState的建構函式中有如下程式碼:

        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.windowTypeToLayerLw(//使用依附視窗的型別
                    attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
                    + WindowManagerService.TYPE_LAYER_OFFSET;
            mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);//計算mSubLayer
            ......
        } else {//非子視窗
            // The multiplier here is to reserve space for multiple
            // windows in the same type layer.
            mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
                    * WindowManagerService.TYPE_LAYER_MULTIPLIER
                    + WindowManagerService.TYPE_LAYER_OFFSET;
            mSubLayer = 0;
            ......
        }

如果視窗型別是子視窗,則使用它所依附的視窗型別來計算mBaseLayer,否則使用視窗型別來計算mBaseLayer。計算的方法是先呼叫mPolicy.windowTypeToLayerLw方法將視窗的型別轉化成一個基數,然後再乘以TYPE_LAYER_MULTIPLIER(10000),最後加上TYPE_LAYER_OFFSET(1000),我們先來看看windowTypeToLayerLw函式是如果根據型別返回一個基數的。

    public int windowTypeToLayerLw(int type) {
        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {//應用視窗
            return 2;
        }
        switch (type) {
        case TYPE_PRIVATE_PRESENTATION:
            return 2;
        case TYPE_WALLPAPER:
            // wallpaper is at the bottom, though the window manager may move it.
            return 2;
        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_DREAM:
            // used for Dreams (screensavers with TYPE_DREAM windows)
            return 10;
        case TYPE_SYSTEM_ALERT:
            // like the ANR / app crashed dialogs
            return 11;
        case TYPE_INPUT_METHOD:
            // on-screen keyboards and other such input method user interfaces go here.
            return 12;
        case TYPE_INPUT_METHOD_DIALOG:
            // on-screen keyboards and other such input method user interfaces go here.
            return 13;
        case TYPE_KEYGUARD_SCRIM:
            // the safety window that shows behind keyguard while keyguard is starting
            return 14;
        case TYPE_STATUS_BAR_SUB_PANEL:
            return 15;
        case TYPE_STATUS_BAR:
            return 16;
        case TYPE_STATUS_BAR_PANEL:
            return 17;
        case TYPE_KEYGUARD_DIALOG:
            return 18;
        case TYPE_VOLUME_OVERLAY:
            // the on-screen volume indicator and controller shown when the user
            // changes the device volume
            return 19;
        case TYPE_SYSTEM_OVERLAY:
            // the on-screen volume indicator and controller shown when the user
            // changes the device volume
            return 20;
        case TYPE_NAVIGATION_BAR:
            // the navigation bar, if available, shows atop most things
            return 21;
        case TYPE_NAVIGATION_BAR_PANEL:
            // some panels (e.g. search) need to show on top of the navigation bar
            return 22;
        case TYPE_SYSTEM_ERROR:
            // system-level error dialogs
            return 23;
        case TYPE_MAGNIFICATION_OVERLAY:
            // used to highlight the magnified portion of a display
            return 24;
        case TYPE_DISPLAY_OVERLAY:
            // used to simulate secondary display devices
            return 25;
        case TYPE_DRAG:
            // the drag layer: input for drag-and-drop is associated with this window,
            // which sits above all other focusable windows
            return 26;
        case TYPE_ACCESSIBILITY_OVERLAY:
            // overlay put by accessibility services to intercept user interaction
            return 27;
        case TYPE_SECURE_SYSTEM_OVERLAY:
            return 28;
        case TYPE_BOOT_PROGRESS:
            return 29;
        case TYPE_POINTER:
            // the (mouse) pointer layer
            return 30;
        }
        Log.e(TAG, "Unknown window type: " + type);
        return 2;
    }

這個方法很簡單就是根據型別返回一個基數。

WindowState中的成員變數mSubLayer只有在視窗是子視窗的時候才有作用,它表示在視窗和父視窗之間的相對位置。程式碼如下

    public int subWindowTypeToLayerLw(int type) {
        switch (type) {
        case TYPE_APPLICATION_PANEL:
        case TYPE_APPLICATION_ATTACHED_DIALOG:
            return APPLICATION_PANEL_SUBLAYER;//等於1
        case TYPE_APPLICATION_MEDIA:
            return APPLICATION_MEDIA_SUBLAYER;//等於-2
        case TYPE_APPLICATION_MEDIA_OVERLAY:
            return APPLICATION_MEDIA_OVERLAY_SUBLAYER;//等於-1
        case TYPE_APPLICATION_SUB_PANEL:
            return APPLICATION_SUB_PANEL_SUBLAYER;//等於2
        case TYPE_APPLICATION_ABOVE_SUB_PANEL:
            return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;//等於3
        }
        Log.e(TAG, "Unknown sub-window type: " + type);
        return 0;
    }

理解了mBaseLayer和mSubLayer後,我們再來看看mLayer是如何計算出來的,是通過assignLayersLocked方法:

    private final void assignLayersLocked(WindowList windows) {
        int N = windows.size();
        int curBaseLayer = 0;
        int curLayer = 0;
        int i;

        boolean anyLayerChanged = false;

        for (i=0; i<N; i++) {
            final WindowState w = windows.get(i);
            final WindowStateAnimator winAnimator = w.mWinAnimator;
            boolean layerChanged = false;
            int oldLayer = w.mLayer;
            if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
                    || (i > 0 && w.mIsWallpaper)) {//如果視窗的mBaseLayer和前一個相同、或者是輸入法和桌布視窗
                curLayer += WINDOW_LAYER_MULTIPLIER;
                w.mLayer = curLayer;
            } else {
                curBaseLayer = curLayer = w.mBaseLayer;
                w.mLayer = curLayer;
            }
            if (w.mLayer != oldLayer) {//層級發生改變
                layerChanged = true;
                anyLayerChanged = true;
            }
            final AppWindowToken wtoken = w.mAppToken;
            oldLayer = winAnimator.mAnimLayer;//後面都是調整mAnimLayerd的值
            if (w.mTargetAppToken != null) {
                winAnimator.mAnimLayer =
                        w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
            } else if (wtoken != null) {
                winAnimator.mAnimLayer =
                        w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
            } else {
                winAnimator.mAnimLayer = w.mLayer;
            }
            if (w.mIsImWindow) {
                winAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
            } else if (w.mIsWallpaper) {
                winAnimator.mAnimLayer += mWallpaperAnimLayerAdjustment;
            }
            if (winAnimator.mAnimLayer != oldLayer) {
                layerChanged = true;
                anyLayerChanged = true;
            }
            final TaskStack stack = w.getStack();
            if (layerChanged && stack != null && stack.isDimming(winAnimator)) {
                // Force an animation pass just to update the mDimLayer layer.
                scheduleAnimationLocked();
            }
        }

        if (mAccessibilityController != null && anyLayerChanged
                && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
            mAccessibilityController.onWindowLayersChangedLocked();
        }
    }

在呼叫assignLayersLocked函式之前,WindowList中的視窗其實已經排好序了,前面呼叫的函式addWindowToListInOrderLocked就是插入視窗到合適的位置,assignLayersLocked函式並不會改變視窗的位置,只是根據視窗的位置計算mLayer的值。

調整方法是從最底層的視窗開始,具有相同的mBaseLayer的值作為一組,每組視窗的mLayer的值從mBaseLayer的值開始,依次加上WINDOW_LAYER_MULTIPLIER(等於5),這樣做的目的是在同層的視窗中每隔一個視窗就留下4個空位,方便下次插入新視窗。

這個方法還對輸入法和桌布視窗做了特殊處理。這兩類視窗和它插入位置前面的視窗處於一個層級,而不是根據他們的mBaseLayer值計算。(就是前面說的當輸入法和桌布出現是在當前Activity的視窗之間的)。

三、插入視窗的位置

在addWindow函式中我們會呼叫addAppWindowToListLocked來確定視窗的位置,現在我們來看下這個函式。

    private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
        if (win.mAttachedWindow == null) {//非子視窗
            final WindowToken token = win.mToken;
            int tokenWindowsPos = 0;
            if (token.appWindowToken != null) {//應用視窗的頂層視窗
                tokenWindowsPos = addAppWindowToListLocked(win);
            } else {
                addFreeWindowToListLocked(win);//系統視窗
            }
            if (addToToken) {
                if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
                token.windows.add(tokenWindowsPos, win);
            }
        } else {
            addAttachedWindowToListLocked(win, addToToken);//新增子視窗
        }

        if (win.mAppToken != null && addToToken) {
            win.mAppToken.allAppWindows.add(win);
        }
    }

上面這個函式根據視窗的型別,應用頂層視窗,系統視窗,子視窗。

我們現在分別對這三類視窗的處理方法進行解析,先來看插入Activity頂層視窗的addAppWindowToListLocked

1.插入Activity頂層方法的addAppWindowToListLocked方法

addAppWindowToListLocked方法先判斷系統中是否存在和待插入的視窗是否有相同的Token,如果有代表它不是Activity的第一個視窗,因此再判斷這個視窗的型別是不是TYPE_BASE_APPLICATION,如果是這類視窗需要放在所有和它相同Token的視窗下面,否則在判斷這個應用的啟動視窗是否位於最前面(說明正在啟動),如果是放在啟動視窗的下面。如果不是下面兩種情況,則尋找同一應用中位置最高的視窗,然後插在它上面,這表示加入的視窗將覆蓋在前面的視窗之上。

下面是部分程式碼,

    private int addAppWindowToListLocked(final WindowState win) {
        final IWindow client = win.mClient;
        final WindowToken token = win.mToken;
        final DisplayContent displayContent = win.getDisplayContent();
        if (displayContent == null) {
            // It doesn't matter this display is going away.
            return 0;
        }

        final WindowList windows = win.getWindowList();
        final int N = windows.size();
        WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
        int tokenWindowsPos = 0;
        int windowListPos = tokenWindowList.size();
        if (!tokenWindowList.isEmpty()) {//如果有,說明它不是第一個視窗
            // If this application has existing windows, we
            // simply place the new window on top of them... but
            // keep the starting window on top.
            if (win.mAttrs.type == TYPE_BASE_APPLICATION) {//放在和它相同Token的視窗下面
                // Base windows go behind everything else.
                WindowState lowestWindow = tokenWindowList.get(0);//第一個0,代表最底層的window
                placeWindowBefore(lowestWindow, win);//放在這個window前面
                tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
            } else {
                AppWindowToken atoken = win.mAppToken;
                WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
                if (atoken != null && lastWindow == atoken.startingWindow) {
                    placeWindowBefore(lastWindow, win);
                    tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
                } else {
                    int newIdx = findIdxBasedOnAppTokens(win);//尋找同一token位置最前面的window
                    //there is a window above this one associated with the same
                    //apptoken note that the window could be a floating window
                    //that was created later or a window at the top of the list of
                    //windows associated with this token.
                    windows.add(newIdx + 1, win);//插在它前面
                    if (newIdx < 0) {
                        // No window from token found on win's display.
                        tokenWindowsPos = 0;
                    } else {
                        tokenWindowsPos = indexOfWinInWindowList(
                                windows.get(newIdx), token.windows) + 1;
                    }
                    mWindowsChanged = true;
                }
            }
            return tokenWindowsPos;
        }

我們再來看幾個函式

placeWindowBefore函式就是插入到windows這個位置前

    private void placeWindowBefore(WindowState pos, WindowState window) {
        final WindowList windows = pos.getWindowList();
        int i = windows.indexOf(pos);
        if (i < 0) {
            Slog.w(TAG, "placeWindowBefore: Unable to find " + pos + " in " + windows);
            i = 0;
        }
        windows.add(i, window);
        mWindowsChanged = true;
    }

findIdxBasedOnAppTokens函式就是尋找相同token的最前面的window,所以要注意遍歷迴圈的時候是從window的size最大的時候反過來遍歷的。

    private int findIdxBasedOnAppTokens(WindowState win) {
        WindowList windows = win.getWindowList();
        for(int j = windows.size() - 1; j >= 0; j--) {
            WindowState wentry = windows.get(j);
            if(wentry.mAppToken == win.mAppToken) {
                return j;
            }
        }
        return -1;
    }


繼續看這個函式,如果系統中不存在和視窗具有相同token的視窗(說明Activity剛啟動,第一個視窗還沒有建立完成),這時就會遍歷系統所有的task以及task中包含的AppWindowToken,找到視窗的位置,再在task中排在本視窗前面的視窗中,找出離自己最近的,並且APPWindowToken的視窗列表不為NULL的視窗,插入到它的最後一個子視窗後面。如果前面的視窗的列表也都為NULL,則尋找排在本視窗後面的第一個包含有視窗物件的APPWindowToken,把本視窗插在前面。

        WindowState pos = null;

        final ArrayList<Task> tasks = displayContent.getTasks();
        int taskNdx;
        int tokenNdx = -1;
        for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
            AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
            for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
                final AppWindowToken t = tokens.get(tokenNdx);
                if (t == token) {
                    --tokenNdx;
                    if (tokenNdx < 0) {
                        --taskNdx;
                        if (taskNdx >= 0) {
                            tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1;
                        }
                    }
                    break;
                }

                // We haven't reached the token yet; if this token
                // is not going to the bottom and has windows on this display, we can
                // use it as an anchor for when we do reach the token.
                tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
                if (!t.sendingToBottom && tokenWindowList.size() > 0) {
                    pos = tokenWindowList.get(0);
                }
            }
            if (tokenNdx >= 0) {
                // early exit
                break;
            }
        }

        // We now know the index into the apps.  If we found
        // an app window above, that gives us the position; else
        // we need to look some more.
        if (pos != null) {
            // Move behind any windows attached to this one.
            WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
            if (atoken != null) {
                tokenWindowList =
                        getTokenWindowsOnDisplay(atoken, displayContent);
                final int NC = tokenWindowList.size();
                if (NC > 0) {
                    WindowState bottom = tokenWindowList.get(0);
                    if (bottom.mSubLayer < 0) {
                        pos = bottom;
                    }
                }
            }
            placeWindowBefore(pos, win);
            return tokenWindowsPos;
        }

        // Continue looking down until we find the first
        // token that has windows on this display.
        for ( ; taskNdx >= 0; --taskNdx) {
            AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
            for ( ; tokenNdx >= 0; --tokenNdx) {
                final AppWindowToken t = tokens.get(tokenNdx);
                tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
                final int NW = tokenWindowList.size();
                if (NW > 0) {
                    pos = tokenWindowList.get(NW-1);
                    break;
                }
            }
            if (tokenNdx >= 0) {
                // found
                break;
            }
        }

        if (pos != null) {
            // Move in front of any windows attached to this
            // one.
            WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
            if (atoken != null) {
                final int NC = atoken.windows.size();
                if (NC > 0) {
                    WindowState top = atoken.windows.get(NC-1);
                    if (top.mSubLayer >= 0) {
                        pos = top;
                    }
                }
            }
            placeWindowAfter(pos, win);
            return tokenWindowsPos;
        }

        ......

如果前面視窗的APPWindowToken的視窗列表也為空,則重新遍歷整個視窗,然後根據mBaseLayer的值來確定視窗的位置。

        final int myLayer = win.mBaseLayer;
        int i;
        for (i = N - 1; i >= 0; --i) {
            WindowState w = windows.get(i);
            if (w.mBaseLayer <= myLayer) {
                break;
            }
        }
        if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
                "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N);
        windows.add(i + 1, win);
        mWindowsChanged = true;
        return tokenWindowsPos;


2. 插入子視窗的addAttachedWindowToListLocked函式

addAttachedWindowToListLocked方法,如果mSubLayer大於0的子視窗,按mSubLayer的值大小插入到有相同WindowToken的子視窗的合適位置中,如果mSubLayer相同,插入已插入視窗的下層位置,如果mSubLayer小於0,如果還存在mSubLayer小於0,並且大於等於該視窗的mSubLayer的值的子視窗,則插入到該子視窗之下,否則插入到子視窗所依附的視窗下面。

    private void addAttachedWindowToListLocked(final WindowState win, boolean addToToken) {
        final WindowToken token = win.mToken;
        final DisplayContent displayContent = win.getDisplayContent();
        if (displayContent == null) {
            return;
        }
        final WindowState attached = win.mAttachedWindow;

        WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);

        // Figure out this window's ordering relative to the window
        // it is attached to.
        final int NA = tokenWindowList.size();
        final int sublayer = win.mSubLayer;
        int largestSublayer = Integer.MIN_VALUE;
        WindowState windowWithLargestSublayer = null;
        int i;
        for (i = 0; i < NA; i++) {
            WindowState w = tokenWindowList.get(i);
            final int wSublayer = w.mSubLayer;
            if (wSublayer >= largestSublayer) {
                largestSublayer = wSublayer;
                windowWithLargestSublayer = w;
            }
            if (sublayer < 0) {
                // For negative sublayers, we go below all windows
                // in the same sublayer.
                if (wSublayer >= sublayer) {
                    if (addToToken) {
                        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
                        token.windows.add(i, win);
                    }
                    placeWindowBefore(wSublayer >= 0 ? attached : w, win);
                    break;
                }
            } else {
                // For positive sublayers, we go above all windows
                // in the same sublayer.
                if (wSublayer > sublayer) {
                    if (addToToken) {
                        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
                        token.windows.add(i, win);
                    }
                    placeWindowBefore(w, win);
                    break;
                }
            }
        }
        if (i >= NA) {
            if (addToToken) {
                if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
                token.windows.add(win);
            }
            if (sublayer < 0) {
                placeWindowBefore(attached, win);
            } else {
                placeWindowAfter(largestSublayer >= 0
                                 ? windowWithLargestSublayer
                                 : attached,
                                 win);
            }
        }
    }


3.插入系統視窗的addFreeWindowToListLocked

addFreeWindowToListLocked方法簡單,只是遍歷同一顯示裝置上的Windows,比較mBaseLayer值的大小,插入合適位置。

    private void addFreeWindowToListLocked(final WindowState win) {
        final WindowList windows = win.getWindowList();

        // Figure out where window should go, based on layer.
        final int myLayer = win.mBaseLayer;
        int i;
        for (i = windows.size() - 1; i >= 0; i--) {
            if (windows.get(i).mBaseLayer <= myLayer) {
                break;
            }
        }
        i++;
        if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
                "Free window: Adding window " + win + " at " + i + " of " + windows.size());
        windows.add(i, win);
        mWindowsChanged = true;
    }

這篇部落格我們分析了window插入到什麼位置,以及mLayer的計算。但是具體裡面有很多變數,stack task windows等,不是很熟悉,下篇部落格我們主要分析這。