WMS中的Z order

轉(zhuǎn)載請(qǐng)標(biāo)注: http://www.itdecent.cn/p/5e802482caa4
始終覺得要理解一個(gè)龐大的東西,需要將它分解,從部分理解開始,慢慢理解了,那么這個(gè)龐大的東西也就理解。

在看這篇文章之前,非常建議參考AMS/WMS/APP 中Token惟一性, 因?yàn)檫@里面會(huì)涉及到各種Token,稍不注意就不知道具體的token是指什么了。

再把這張圖貼在這里, 這樣看代碼時(shí),對(duì)照著看就更容易理解了。


Token的惟一性

注意: 本篇以Launcher啟動(dòng)一個(gè)App (MainActivity)時(shí),來學(xué)習(xí)Z order. 另外由于在啟動(dòng)一個(gè)App時(shí)會(huì)有一個(gè)starting Window, 略過這部分。

一、加入Window到WMS的窗口堆棧

public int addWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        InputChannel outInputChannel) {

    final int type = attrs.type;
    //attrs即Activity的Window中的LayoutParams, 這里值是FIRST_APPLICATION_WINDOW

    synchronized(mWindowMap) {
        //displayId為具體的顯示屏,一般沒有外接都是默認(rèn)的顯示屏
        //DisplayContent為顯示屏上顯示的內(nèi)容,主要是一些Window對(duì)應(yīng)的WindowState
        final DisplayContent displayContent = getDisplayContentLocked(displayId);

        //如果WindowMap已經(jīng)加入過了,就直接返回,不用再生成對(duì)應(yīng)的WindowState了
        if (mWindowMap.containsKey(client.asBinder())) {
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }

        boolean addToken = false;
        WindowToken token = mTokenMap.get(attrs.token); 
        //獲得Activity對(duì)應(yīng)在WMS中的WindowToken, 在這里已經(jīng)不為空了,具體見[2. WMS中的token]
        AppWindowToken atoken = null;  
        if (token == null) {
         } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
            atoken = token.appWindowToken;  //獲得AppWindowToken
        } else (XXXX){        
        }
        
        //生成對(duì)應(yīng)的WindowState用于保存Window信息
        WindowState win = new WindowState(this, session, client, token,
                attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);

        res = WindowManagerGlobal.ADD_OKAY;
   
        if (addToken) {
            mTokenMap.put(attrs.token, token); //這個(gè)在addAppToken中已經(jīng)加入過了,所以不用加入了
        }
        win.attach();  //Session里 mNumWindow加1,表示現(xiàn)在總共有多少window了
        mWindowMap.put(client.asBinder(), win);  //儲(chǔ)存WindowState

        if (type == TYPE_INPUT_METHOD) {//輸入法窗口,這里不討論
        } else if (type == TYPE_INPUT_METHOD_DIALOG) {
        } else {
            addWindowToListInOrderLocked(win, true);  //將WindowState加入到窗口堆棧中
        }
    }
    return res;
}

1.1 Window的類型

addWindow最后會(huì)根據(jù)不同的Window類型做不同的處理,那這些Window類型是什么呢?

attrs.type 這個(gè)指明 Window的類型, 它的值在如下代碼中初始化

private final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();

public LayoutParams() {
    super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    type = TYPE_APPLICATION;  //默認(rèn)為TYPE_APPLICATION類型
    format = PixelFormat.OPAQUE;
}

由代碼可知,LayoutParams默認(rèn)的Type是TYPE_APPLICATION = 2; 即普通的應(yīng)用窗口

但是會(huì)在handleResumeActivity被修改掉,如下

--- in handleResumeActivity

WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; //修改窗口類型
wm.addView(decor, l);

但是在addView之前修改了窗口類型為TYPE_BASE_APPLICATION = 1類型,這個(gè)類型是基類,是Activity中最底層的窗口,換句話說,其它的窗口都會(huì)疊加在該窗口之上,比如Dialog窗口(它使用默認(rèn)的TYPE_APPLICATION)。 這也說明type值越大,它就越在上面。

Android定義的窗口值如下:

  • 應(yīng)用程序窗口
定義 意義
FIRST_APPLICATION_WINDOW 1 第一個(gè)窗口,也就是TYPE_BASE_APPLICATION窗口
TYPE_BASE_APPLICATION 1 Activity的基窗口
TYPE_BASE_APPLICATION 2 默認(rèn)窗口類型
TYPE_APPLICATION_STARTING 3 應(yīng)用程序啟動(dòng)過程中的中間窗口,當(dāng)真下窗口適配完會(huì)關(guān)閉該窗口
LAST_APPLICATION_WINDOW 99 應(yīng)用程序最后一個(gè)窗口

Question: 如果將type設(shè)為大于99會(huì)怎么呢?

在WMS的addWindow會(huì)對(duì)type進(jìn)行權(quán)限檢查

int res = mPolicy.checkAddPermission(attrs, appOp);

即如果type不屬于 應(yīng)用窗口/子窗口/系統(tǒng)窗口,直接會(huì)報(bào)異常。


而真正將Window加入到窗口堆棧的函數(shù)是 addWindowToListInOrderLocked

1.2 addWindowToListInOrderLocked

private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
    if (win.mAttachedWindow == null) { //新啟動(dòng)的Activity, 沒有attach的Window
        final WindowToken token = win.mToken; //WindowState中的mToken
        int tokenWindowsPos = 0;
        if (token.appWindowToken != null) { //這里顯然不為null,在生成AppWindowToken時(shí)指向 見圖p2
            tokenWindowsPos = addAppWindowToListLocked(win); //進(jìn)入這個(gè)分支
        } 
}
private int addAppWindowToListLocked(final WindowState win) {
    final DisplayContent displayContent = win.getDisplayContent();
    final IWindow client = win.mClient;
    final WindowToken token = win.mToken;

    final WindowList windows = displayContent.getWindowList(); //得到DisplayContent中所有的WindowState, 

此時(shí)窗口堆棧順序如下:

窗口堆棧順序
    WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); 
    //當(dāng)前Activity MainActivity是第一次啟動(dòng),所以它并沒有其它Window

    final ArrayList<Task> tasks = displayContent.getTasks(); //這里會(huì)得到所有的task
此時(shí)的Stack_Task的關(guān)系
tasks的值

接著看addAppWindowToListLocked后面的代碼

    int taskNdx;
    int tokenNdx = -1;
    for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { //當(dāng)taskNdx = 1時(shí)
        AppTokenList tokens = tasks.get(taskNdx).mAppTokens;  //此時(shí)的task里僅有一個(gè)MainActivity中的AppWindowToken
        for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { //tokenNdx = 0
            final AppWindowToken t = tokens.get(tokenNdx); //此時(shí)t 為 MainActivity中的AppWindowToken
            if (t == token) { //相等
                --tokenNdx; //此時(shí)tokenNdx = -1
                if (tokenNdx < 0) { //進(jìn)入此分支
                    --taskNdx;  //尋找前一個(gè)task, 此時(shí)taskNdx = 0
                    if (taskNdx >= 0) { //進(jìn)入該分支
                        //Launcher Task中的也僅有一個(gè)App token, 這里減1的目的是當(dāng) add 的時(shí)候,以tokenNdx為索引,加在后面
                        tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1; 
                    }
                }
                break;
            }
        }
        if (tokenNdx >= 0) { //此時(shí)tokenNdx = 0, 直接break掉
            break;
        }
    }


    // Continue looking down until we find the first
   // token that has windows on this display.
    //此時(shí)taskNdx = 0, tokenNdx = 0
    for ( ; taskNdx >= 0; --taskNdx) { 
        AppTokenList tokens = tasks.get(taskNdx).mAppTokens; //獲得Launcher Task中所有的tokens
        for ( ; tokenNdx >= 0; --tokenNdx) {
            final AppWindowToken t = tokens.get(tokenNdx);  //這里獲得的是Launcher的token
            tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); //獲得Launcher token中的WinState
            final int NW = tokenWindowList.size(); //這里NW為1,因?yàn)闆]有其它子窗口
            if (NW > 0) {
                pos = tokenWindowList.get(NW-1);  //直接獲得最后的win state (這里也是Launcher的WinState)
                break;
            }
        }
    }

    if (pos != null) { //pos已經(jīng)不為空了
        WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); //
        if (atoken != null) { //獲得Launcher的 WindowToken
            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); //在Launcher后插入Win (MainActivity)
        return tokenWindowsPos;  //返回
    }
}
此時(shí)窗口堆棧順序

二、Z -order

前面已經(jīng)將Window加入到了窗口堆棧,并不是Z order,下面來看下什么是Z order。

由于前面窗口堆棧已經(jīng)新加入了一個(gè)window, 即窗口堆棧變化了,那么此時(shí)就要計(jì)算 Z order了
具體是在 addWindow的最后了

--- in addWindow()

mLayersController = new WindowLayersController(this);

WMS初始化時(shí)初始化WindowLayersController, 該類主要是為DisplayContent里的Windows也就是Window堆棧指定layer, 也就是計(jì)算它們的Z order。 計(jì)算Z order時(shí),從窗口堆棧的底部到頂部,層級(jí)越高,那么它的 layer 越大,即它的Z order越大。

再繼續(xù)講計(jì)算 Z -order之前先簡(jiǎn)單看下與Z order有關(guān)的幾個(gè)變量, 具體在WindowState中

--- in WindowState

final WindowStateAnimator mWinAnimator;
int mLayer;

final int mBaseLayer;
final int mSubLayer;

WindowStateAnimator 是一個(gè)Window的動(dòng)畫以及對(duì)Surface的操作類,它類中有個(gè)Animator的 Z order值,

  • mLayer
    mLayer是Base的Z -order值,它的值是動(dòng)態(tài)算出來的。
  • mAnimLayer
    它的值是由mLayer算出來的, SurfaceFlinger中的Layer的Z order也就是mAnimLayer。

可以從定義看出 mBaseLayer, mSubLayer是final, 是不可再重新賦值的,所以它們的值是固定的,很顯然是在WindowState初始化時(shí)賦值的。

--- in WindowState構(gòu)造函數(shù)中

        if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) {
          //子窗口,不討論
        } else {
            mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
                    * WindowManagerService.TYPE_LAYER_MULTIPLIER  //該值是10000
                    + WindowManagerService.TYPE_LAYER_OFFSET;   //該值為1000
            mSubLayer = 0;
       }
public int windowTypeToLayerLw(int type) {
   if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
            return 2;
   }
}

以非子窗口為例
此時(shí)mSubLayer=0, 而mBaseLayer與Window類型相關(guān),以本文例子為例, 應(yīng)用程序的窗口映射的z-order為2, 所以經(jīng)過計(jì)算, mBaseLayer就為21000

注意:并不代表 Window type值越大,它的Z -order值越大,type值會(huì)被windowTypeToLayerLw重新映射。

下面來看下是怎么計(jì)算z order值的

mLayersController.assignLayersLocked(displayContent.getWindowList());

final void assignLayersLocked(WindowList windows) {
    int curBaseLayer = 0;  //臨時(shí)的基值
    int curLayer = 0;   //當(dāng)前的值
    boolean anyLayerChanged = false;   //只要一個(gè)Z order值變了,這里就會(huì)為true
    for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
        final WindowState w = windows.get(i);
        boolean layerChanged = false;  //針對(duì)每一個(gè)window

        int oldLayer = w.mLayer;
        if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
            curLayer += WINDOW_LAYER_MULTIPLIER; //WINDOW_LAYER_MULTIPLIER值為5,
        } else {
            curBaseLayer = curLayer = w.mBaseLayer;
        }
        assignAnimLayer(w, curLayer); //設(shè)置Z order值,以及動(dòng)畫的Z order

        if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
            layerChanged = true;
            anyLayerChanged = true;
        }

        if (w.mAppToken != null) {
            mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
                    w.mWinAnimator.mAnimLayer); //獲得應(yīng)用窗口中最大值,注意這里只是針對(duì)應(yīng)用窗口。
        }
        collectSpecialWindows(w);  //收集特殊的窗口

        if (layerChanged) {
            w.scheduleAnimationIfDimming();
        }
    }

    adjustSpecialWindows();  //針對(duì)特殊窗口再做調(diào)整, 不討論

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

三、將 Z order更新到Native層

Z order值是Surface里的概念(SurfaceFlinger中對(duì)應(yīng)的是Layer類), 由于addWindow發(fā)生的很早,此時(shí)并沒有Surface. 所以并不是在這里設(shè)備Z order值的,

準(zhǔn)備的說是在創(chuàng)建Surface過后,

WindowSurfaceController createSurfaceLocked() {
        ...
        //layer stack表示是該Surface顯示在哪個(gè)顯示設(shè)備上,
        final int layerStack = w.getDisplayContent().getDisplay().getLayerStack();
        //將 layerstack與 z -order值(mAnimlayer)設(shè)置到Surface對(duì)應(yīng)的SurfaceFlinger中的Layer中
        mSurfaceController.setPositionAndLayer(mTmpSize.left, mTmpSize.top, layerStack, mAnimLayer);
        mLastHidden = true;
}

最后通過Layer.cpp中的setLayer函數(shù)將Z order值保存到Layer的mCurrentState中的z變量中。

bool Layer::setLayer(uint32_t z) { 
    if (mCurrentState.z == z)
        return false; 
    mCurrentState.sequence++;
    mCurrentState.z = z; 
    mCurrentState.modified = true;
    setTransactionFlags(eTransactionNeeded);
    return true;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容