Android WMS窗口管理

???????WindowManagerService跟ActivityManagerService一樣都是系統(tǒng)核心服務系瓢,通過SystemServer來啟動的腥沽,ActivityManagerService負責Activity的整個生命周期授翻,WindowManagerService負責Activity對應的窗口顯示的整個生命周期贯莺,兩大核心服務各司其職去扣,支撐起了Android Framework的半邊天柱衔。
???????本文主要針對窗口管理及Layer、size計算等邏輯進行分析愉棱,窗口顯示過程分析可以參考文章:
???????Android WMS窗口管理(二)

一.WindowManagerService

???????前面說到唆铐,WMS是在SystemServer內(nèi)部啟動的,簡單看一下啟動邏輯:

private void startOtherServices() {
    ...........................
    WindowManagerService wm = null;
    ...........................
    ...........................
    traceBeginAndSlog("StartWindowManagerService");
    // WMS needs sensor service ready
    ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
    mSensorServiceStart = null;
    wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
    ServiceManager.addService(Context.WINDOW_SERVICE, wm);
    traceEnd();
    ........................
}

???????可以看到奔滑,通過WindowManagerService的main()來創(chuàng)建了對象實例艾岂,然后通過ServiceManager將其加入到實名Binder來支持進程間通信。
???????看一下main()方法的邏輯實現(xiàn):

public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
    DisplayThread.getHandler().runWithScissors(() ->
    sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore, policy), 0);
    return sInstance;
}

???????內(nèi)部創(chuàng)建了WMS實例然后返回朋其,看一下WMS的構造方法:

private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {
    mRoot = new RootWindowContainer(this);
    .................................
    .................................
    mInputManager = inputManager; // Must be before createDisplayContentLocked.
    mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

    mWindowPlacerLocked = new WindowSurfacePlacer(this);
    mPolicy = policy;
    mTaskSnapshotController = new TaskSnapshotController(this);

    LocalServices.addService(WindowManagerPolicy.class, mPolicy);
    .......................................
    mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    mDisplays = mDisplayManager.getDisplays();
    for (Display display : mDisplays) {
        createDisplayContentLocked(display);
    }

    ...................................
    mActivityManager = ActivityManager.getService();
    ......................................
    mAnimator = new WindowAnimator(this);

    LocalServices.addService(WindowManagerInternal.class, new LocalService());
    ..........................
    // Add ourself to the Watchdog monitors.
    Watchdog.getInstance().addMonitor(this);
    .......................
}

???????省略了部分邏輯王浴,在構造方法內(nèi)主要干了如下幾件事:
???????1.創(chuàng)建RootWindowContainer對象脆炎,根窗口容器;
???????2.將mPolicy即PhoneWindowManager加入到LocalServices中叼耙;
???????3.遍歷系統(tǒng)所有的Display腕窥,創(chuàng)建對應DisplayContent粒没;
???????4.將內(nèi)部LocalService加入到LocalServices中筛婉;
???????5.加入Watchdog進行監(jiān)聽;
???????WindowManagerService內(nèi)部的其他邏輯在接下來的分析中涉及到的會一一進行講解癞松;

二.窗口管理

??????? “窗口”是一個抽象的概念爽撒,從用戶的角度來講,其實就是我們看到的“界面”响蓉;從WMS的角度來看硕勿,它是一個WindowState,用于管理和界面有關的狀態(tài)枫甲;從SurfaceFlinger的角度來看源武,它是一個layer,承載著和界面有關的數(shù)據(jù)和屬性粱栖;在Android中,一個窗口會獨占一個Surface,Surface其實就是一個畫布,應用程序通過Canvas在Surface上繪制內(nèi)容;
??????? 本文對從Activity創(chuàng)建到顯示的過程進行分析,前面講到AMS負責Activity的生命周期丁屎,WMS負責Activity對應窗口的顯示的整個生命周期证九,兩者之間緊密配合妈拌,接下來看一下窗口管理涉及的主要類:

1.RootWindowContainer

??????? 在WMS構造方法內(nèi)進行創(chuàng)建,一個設備對應一個RootWindowContainer,負責管理DisplayContent谍咆;

class RootWindowContainer extends WindowContainer<DisplayContent> {
    ................
    private final WindowLayersController mLayersController;
    final WallpaperController mWallpaperController;
    .............................
    RootWindowContainer(WindowManagerService service) {
        mService = service;
        mHandler = new MyHandler(service.mH.getLooper());
        mLayersController = new WindowLayersController(mService);
        mWallpaperController = new WallpaperController(mService);
    }
    ..................
}

??????? RootWindowContainer繼承WindowContainer庇茫,是所有窗口管理類的的基類查坪。

2.DisplayContent

??????? 在WMS構造方法內(nèi)遍歷Display進行創(chuàng)建羔巢,最終是通過RootWindowContainer進行創(chuàng)建,每個Display對應一個DisplayContent歉备,一個設備可對應多個DisplayContent,創(chuàng)建執(zhí)行過程就不看了利凑,直接從DisplayContent的構造方法看:

DisplayContent(Display display, WindowManagerService service,
            WindowLayersController layersController, WallpaperController wallpaperController) {

    mDisplay = display;
    mDisplayId = display.getDisplayId();
    mLayersController = layersController;
    mWallpaperController = wallpaperController;
    ..................
    isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
    mService = service;

    // These are the only direct children we should ever have and they are permanent.
    super.addChild(mBelowAppWindowsContainers, null);
    super.addChild(mTaskStackContainers, null);
    super.addChild(mAboveAppWindowsContainers, null);
    super.addChild(mImeWindowsContainers, null);

    // Add itself as a child to the root container.
    mService.mRoot.addChild(this, null);
}

??????? 從構造方法可以看到日丹,在DisplayContent內(nèi)部管理了4個WindowContainer择示,簡單介紹一下:
???????mBelowAppWindowsContainers:NonAppWindowContainers類型,保存了所有應該顯示到App類窗口的下面的非App類的窗口扒寄,layer設置為0;
??????? mTackStackContainer: 保存了所有與App(Activities)相關的Window拟烫,layer設置為1该编;
??????? mAboveAppWindowContainer: NonAppWindowContainer類型,保存了所有應該顯示到App類窗口的上面的非App類的窗口硕淑,layer設置為2课竣;
??????? mImeWindowContainers: NonAppWindowContainer類型,包含了所有IME window Containers置媳;
???????最終通過mService.mRoot.addChild(this, null)加入到RootWindowContainer內(nèi)進行管理于樟;
???????本文只分析Activity對應的窗口,所以主要涉及mTackStackContainers拇囊,mTackStackContainers用來管理TaskStack迂曲;

3.TaskStack

??????? 前面講到,RootWindowContainer和DisplayContent是在WMS的構造方法內(nèi)部進行創(chuàng)建寂拆,從創(chuàng)建時機來看奢米,應該是提前準備好來對窗口進行管理抓韩,那TaskStack是什么時候創(chuàng)建的呢?先亮答案:是在ActivityStack創(chuàng)建時進行創(chuàng)建鬓长;一起看一下:

ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
            ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
    ............
    mWindowContainerController = createStackWindowController(display.mDisplayId, onTop, mTmpRect2);
    mStackSupervisor.mStacks.put(mStackId, this);
    postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}

???????在創(chuàng)建ActivityStack時主要干了幾件事:
???????1.創(chuàng)建StackWindowController對象谒拴;
???????2.將新創(chuàng)建的ActiivtyStack加入到ActivityStackSuperVisor的mStacks進行管理;
???????3.同時將新創(chuàng)建的ActiivtyStack加入到ActivityDisplay進行管理涉波;

T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
    return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
}

???????在創(chuàng)建StackWindowController時英上,創(chuàng)建了TaskStack;

public StackWindowController(int stackId, StackWindowListener listener,
            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
        super(listener, service);
    mStackId = stackId;

    synchronized (mWindowMap) {
        final DisplayContent dc = mRoot.getDisplayContent(displayId);

        final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
        stack.setController(this);
    }
}

???????1.獲取displayId對應的DisplayContent;
???????2.通過DisplayContent獲取TaskStack啤覆,在addStackToDisplay()內(nèi)部先創(chuàng)建TaskStack苍日,然后將其加入到mTaskStackContainers內(nèi)部進行管理;
???????3.設置 TaskStack 的成員變量 mController 為 StackWindowController窗声;
???????4.設置 StackWindowController 的成員變量 mContainer 為 TaskStack相恃;

4.Task

???????Task是在哪里創(chuàng)建的呢?先亮答案:是在TaskRecord內(nèi)部進行創(chuàng)建笨觅;一起看一下:

void createWindowContainer(boolean onTop, boolean showForAllUsers) {
    final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
    final Configuration overrideConfig = getOverrideConfiguration();
    setWindowContainerController(new TaskWindowContainerController(taskId, this,
            getStack().getWindowContainerController(), userId, bounds, overrideConfig,
            mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
            lastTaskDescription));
}

???????跟TaskStack創(chuàng)建過程類似拦耐,也是先創(chuàng)建TaskWindowContainerController,然后在其內(nèi)部創(chuàng)建Task见剩;

public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
            StackWindowController stackController, int userId, Rect bounds,
            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
            boolean homeTask, boolean toTop, boolean showForAllUsers,
            TaskDescription taskDescription, WindowManagerService service) {
    super(listener, service);
    mTaskId = taskId;
    mHandler = new H(new WeakReference<>(this), service.mH.getLooper());

    synchronized(mWindowMap) {
        final TaskStack stack = stackController.mContainer;
        EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
        final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
                    supportsPictureInPicture, homeTask, taskDescription);
        final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
        stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
    }
}

???????1.先通過StackWindowController的變量mContainer獲取到TaskStack杀糯;
???????2.通過createTask()來獲取Task,并在Task構造方法內(nèi)部執(zhí)行setController()苍苞;
???????3.設置 Task 成員變量 mController 為 TaskWindowContainerController固翰;
???????4.設置 TaskWindowContainerController 的成員變量 mContainer 為Task;
???????5.執(zhí)行stack.addTask()將Task放入TaskStack進行管理羹呵;

5.AppWindowToken

???????AppWindowToken是在哪里創(chuàng)建的呢骂际?先亮答案:是在ActivityRecord內(nèi)部進行創(chuàng)建;一起看一下:

void createWindowContainer() {
    final TaskWindowContainerController taskController = task.getWindowContainerController()

    mWindowContainerController = new AppWindowContainerController(taskController, appToken,
                this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, fullscreen,
                (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
                appInfo.targetSdkVersion, mRotationAnimationHint,
                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L,
                getOverrideConfiguration(), mBounds);
    .........................
}

???????也是先創(chuàng)建AppWindowContainerController担巩,然后在其內(nèi)部創(chuàng)建AppWindowToken方援;

public AppWindowContainerController(TaskWindowContainerController taskController,
            IApplicationToken token, AppWindowContainerListener listener, int index,
            int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
            WindowManagerService service, Configuration overrideConfig, Rect bounds) {
    super(listener, service);
    mHandler = new H(service.mH.getLooper());
    mToken = token;
    synchronized(mWindowMap) {
        final Task task = taskController.mContainer;
        atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
                requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
                alwaysFocusable, this, overrideConfig, bounds);
        task.addChild(atoken, index);
    }
}

???????1.先通過TaskWindowContainerController的變量mContainer獲取到Task;
???????2.通過createAppWindow()來獲取AppWindowToken涛癌,并在AppWindowToken構造方法內(nèi)部執(zhí)行setController()犯戏;
???????3.設置 AppWindowToken 成員變量 mController 為 AppWindowContainerController;
???????4.設置 AppWindowContainerController 的成員變量 mContainer 為AppWindowToken拳话;
???????5.執(zhí)行task.addChild()將AppWindowToken放入Task進行管理先匪;

6.WindowState

???????WindowState對應最終的顯示窗口,一般來說弃衍,一個Actiivty對應一個WindowState呀非,我們知道,在Activity執(zhí)行onResume會通過WindowManager.addView()將Activity對應的DecorView進行顯示,經(jīng)過ViewRootImpl-->Session-->WMS執(zhí)行到addWindow()方法岸裙,詳細過程就不贅述了猖败,先看addWindow()方法:

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
    .........................
    WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
    atoken = token.asAppWindowToken();
    .........................
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
    .................
    win.mToken.addWindow(win);
    ................
}

???????1.根據(jù)參數(shù)獲取到對應的AppWindowToken(前面已經(jīng)創(chuàng)建了)降允;
???????2.將token作為參數(shù)傳入創(chuàng)建WindowState恩闻;
???????3.將WindowState加入到AppWindowToken內(nèi)部進行管理;

7.總結

???????前面邏輯處理涉及到AMS剧董,用一張圖來總結一下相互之間的邏輯關系:


image.png

三.窗口顯示Layer分配

???????不同的窗口對應的不同的顯示layer幢尚,比如狀態(tài)欄要顯示在應用窗口之上,所以需要提前分配好要顯示的layer翅楼,然后通知給SurfaceFlinger尉剩,最后在繪制合成時保證顯示正常;
???????前面說到毅臊,WindowState對應最終的顯示窗口理茎,所以layer應該是在WindowState內(nèi)部體現(xiàn),一起看一下:

1.WindowState

WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
           WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
           int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
    .....................
    .....................

    if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
        mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
        mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
        mIsChildWindow = true;
        parentWindow.addChild(this, sWindowSubLayerComparator);
        mLayoutAttached = mAttrs.type !=
                    WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
         mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
                    || parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
         mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
    } else {
        mBaseLayer = mPolicy.getWindowLayerLw(this)* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
        mSubLayer = 0;
        mIsChildWindow = false;
        mLayoutAttached = false;
        mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
                    || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
        mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
    }
    .........................

    mWinAnimator = new WindowStateAnimator(this);
    ..........................
    mLayer = 0;
}

???????1.mBaseLayer:基礎layer褂微,由窗口類型(通過policy轉(zhuǎn)換后)×10000+1000計算得到功蜓,相同type對應WindowState的mBaseLayer都是一樣的,窗口類型對應區(qū)間如下:
??????????????應用窗口:層級范圍是1~99
??????????????子窗口:層級范圍是1000~1999
??????????????系統(tǒng)窗口:層級范圍是2000~2999
???????2.mLayer:經(jīng)過計算得到最終的窗口顯示layer值宠蚂;
???????3.創(chuàng)建WindowState對應的WindowStateAnimator,并初始化將mLayer設置為0童社;
???????在創(chuàng)建WindowState進行一些基本計算求厕,那最終的值是在什么地方確定的呢?
???????還得回到WMS的addWindow()方法扰楼,在創(chuàng)建完WindowState后呀癣,會執(zhí)行以下邏輯:

displayContent.assignWindowLayers(false /* setLayoutNeeded */)

???????根據(jù)調(diào)用關系,會執(zhí)行到WindowLayersController的assignWindowLayers()方法:

2.WindowLayersController

final void assignWindowLayers(DisplayContent dc) {
    reset();
    dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
    adjustSpecialWindows();
}
2.1.reset()
private void reset() {
    mPinnedWindows.clear();
    mInputMethodWindows.clear();
    .............

    mCurBaseLayer = 0;
    mCurLayer = 0;
    mAnyLayerChanged = false;

    mHighestApplicationLayer = 0;
    mHighestDockedAffectedLayer = 0;
    mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
    mImeTarget = mService.mInputMethodTarget;
    mAboveImeTarget = false;
    mAboveImeTargetAppWindows.clear();
}

???????將mCurBaseLayer和mCurLayer等置為0弦赖;

2.2.mAssignWindowLayersConsumer

private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
    boolean layerChanged = false;

    int oldLayer = w.mLayer;
    if (w.mBaseLayer == mCurBaseLayer) {
        mCurLayer += WINDOW_LAYER_MULTIPLIER;
    } else {
        mCurBaseLayer = mCurLayer = w.mBaseLayer;
    }
    assignAnimLayer(w, mCurLayer);

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

    if (w.mAppToken != null) {
        mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
                    w.mWinAnimator.mAnimLayer);
    }
    if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
        mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
                    w.mWinAnimator.mAnimLayer);
    }
    if (w.getAppToken() != null && StackId.isResizeableByDockedStack(w.getStackId())) {
        mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
                    w.mWinAnimator.mAnimLayer);
    }

    collectSpecialWindows(w);

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

???????mCurBaseLayer: 記錄當前計算的對應type窗口的baseLayer项栏;mCurLayer: 最終需要設置的layer值;
???????通過處理邏輯可以看到蹬竖,相同baseLayer的窗口會每次加WINDOW_LAYER_MULTIPLIER(5)沼沈,即:相同type的窗口layer值之間呈+5遞增;

2.2.1.assignAnimLayer(w, mCurLayer)
private void assignAnimLayer(WindowState w, int layer) {
    w.mLayer = layer;
    w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment() + w.getSpecialWindowAnimLayerAdjustment();
       ....................
}

???????通過assignAnimLayer()將剛才計算得到的mCurLayer設置給WindowState的mLayer币厕;如果是App窗口列另,mAnimLayer與mLayer值是相同的;

2.2.2. collectSpecialWindows(w)
private void collectSpecialWindows(WindowState w) {
    ...........................
    if (w.mIsImWindow) {
        mInputMethodWindows.add(w);
        return;
    }
       

    final int stackId = w.getAppToken() != null ? w.getStackId() : INVALID_STACK_ID;
    if (stackId == PINNED_STACK_ID) {
        mPinnedWindows.add(w);
    }
    ..........
}

???????收集特殊的窗口旦装,比如:輸入法页衙、畫中畫窗口等;

2.3.adjustSpecialWindows()

private void adjustSpecialWindows() {
    int layer = mHighestDockedAffectedLayer +  TYPE_LAYER_OFFSET;
    ..........................
    layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER)
    ............................
    while (!mInputMethodWindows.isEmpty()) {
        layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
    }
}

private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
    if (win != null) {
        assignAnimLayer(win, layer);
        // Make sure we leave space in-between normal windows for dims and such.
        layer += WINDOW_LAYER_MULTIPLIER;
    }
    return layer;
}

???????最后調(diào)整特殊窗口的layer值;調(diào)整完后在什么地方來設置到SurfaceFlinger呢店乐?后續(xù)會講到艰躺。

四.窗口size計算

???????在窗口顯示前需要先知道要顯示窗口的大小及位置,那么這里就要用到窗口大小及位置計算眨八,接下來先看一下系統(tǒng)區(qū)域大小定義:
???????overScan:邊緣區(qū)域(四周有一圈黑色的區(qū)域)描滔,該區(qū)域是顯示屏的一部分,但通常不顯示畫面踪古;
???????overscanScreen:包含了overscan區(qū)域卵渴,相當于整個屏幕大小阱驾;
???????RestrictedOverscanScreen:不包含導航欄恨统;
???????RestrictedScreen:不包含屏幕上的overscan區(qū)域,不包含導航條枕扫;
???????UnrestrictedScreen:不包含overscan區(qū)域陪腌,包含狀態(tài)條和導航欄;
???????StableFullScreen:包含狀態(tài)欄烟瞧,不包含導航欄诗鸭;
???????Decor:不包含狀態(tài)欄,不包含導航欄参滴,包含輸入法的區(qū)域强岸;
???????Current:不包含狀態(tài)欄,不包含導航欄砾赔,不包含輸入法的區(qū)域蝌箍;
???????我們知道,窗口在顯示時在ViewRootImpl內(nèi)會調(diào)用到relayoutWindow()暴心,最終會調(diào)用到WMS的relayoutWindow()妓盲,在內(nèi)部會執(zhí)行到以下邏輯:

mWindowPlacerLocked.performSurfacePlacement(true /* force */)

???????跟著調(diào)用關系會執(zhí)行到DisplayContent的applySurfacecChangesTransaction(),內(nèi)部會調(diào)用到performLayout():

void performLayout(boolean initial, boolean updateInputWindows) {
    .............................
    final int dw = mDisplayInfo.logicalWidth;
    final int dh = mDisplayInfo.logicalHeight;

    mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation,
                getConfiguration().uiMode);
    ..................
    mService.mPolicy.getContentRectLw(mContentRect);
    ..................
    // First perform layout of any root windows (not attached to another window).
    forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
    .......................
}

1.beginLayoutLw()

???????獲取到Display的寬度和高度专普,調(diào)用PhoneWindowManager的beginLayoutLw()來初始化系統(tǒng)區(qū)域大小(上面提到的)以及計算其他值悯衬;

public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
                              int displayRotation, int uiMode) {
    .......................
    mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
    mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
    mSystemRight = displayWidth;
    mSystemBottom = displayHeight;
    mUnrestrictedScreenLeft = overscanLeft;
    mUnrestrictedScreenTop = overscanTop;
    mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
    mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
    ...............................
    final Rect pf = mTmpParentFrame;
    final Rect df = mTmpDisplayFrame;
    final Rect of = mTmpOverscanFrame;
    final Rect vf = mTmpVisibleFrame;
    final Rect dcf = mTmpDecorFrame;
    pf.left = df.left = of.left = vf.left = mDockLeft;
    pf.top = df.top = of.top = vf.top = mDockTop;
    pf.right = df.right = of.right = vf.right = mDockRight;
    pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
    dcf.setEmpty(); 
    .................................
    boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
                    displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,navAllowedHidden, statusBarExpandedNotKeyguard);
    updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
}

???????pf(parentFrame):表示窗口父窗口的大小檀夹;
???????df(deviceFrame):設備的屏幕大薪畲帧;
???????of(OverScanFrame):設備屏幕大小击胜、和df值一樣亏狰;
???????cf(ContentFrame):窗口內(nèi)容區(qū)域大小偶摔;
???????vf(VisibleFrame):窗口可見內(nèi)容區(qū)域大邢就佟;
???????dcf(DecorFrame):裝飾區(qū)域大小,除去狀態(tài)欄和導航欄的區(qū)域策州;
???????接著看一下layoutStatusBar()實現(xiàn):

private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
            boolean isKeyguardShowing) {
    if (mStatusBar != null) {
         // apply any navigation bar insets
         pf.left = df.left = of.left = mUnrestrictedScreenLeft;
         pf.top = df.top = of.top = mUnrestrictedScreenTop;
         pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
         pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
                    + mUnrestrictedScreenTop;
         vf.left = mStableLeft
         vf.top = mStableTop;
         vf.right = mStableRight;
         vf.bottom = mStableBottom;
         mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                    vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
                    dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */)
         ..................
         mContentTop = mVoiceContentTop = mCurTop = mDockTop;
         mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
         mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
         mContentRight = mVoiceContentRight = mCurRight = mDockRight;
    }
}

???????在計算完pf瘸味、df等值時,調(diào)用到WindowState的computeFrameLw():

public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
            Rect outsetFrame) {
    ...................
    mOverscanFrame.set(overscanFrame);
    mContentFrame.set(contentFrame);
    mVisibleFrame.set(visibleFrame);
    mDecorFrame.set(decorFrame);
    mStableFrame.set(stableFrame);
    ....................
    mContainingFrame.set(parentFrame);
    mDisplayFrame.set(displayFrame);
    mFrame.set(left, top, left + width, top + height);
    ..................
}

???????在computeFrameLw()內(nèi)部主要是設置了以下幾個變量:
???????mFrame:描述窗口的位置和尺寸够挂;
???????mContainingFrame和mParentFrame:這兩個矩形相同,保存了pf參數(shù)旁仿;
???????mDisplayFrame:保存了df參數(shù);
???????mContentFrame和mContentInsets:mContentFrame表示當前窗口中顯示內(nèi)容的區(qū)域孽糖,mContentInsets表示mContentFrame與mFrame的4條邊界之間的距離枯冈;
???????mVisibleFrame和mVisibleInsets:mVisibleFrame表示當前不被系統(tǒng)遮擋的區(qū)域,mVisibleInsets表示mVisibleFrame與mFrame的4條邊界之間的距離办悟;

2.layoutWindowLw()

???????在DisplayContent內(nèi)部的performLayout()會執(zhí)行到以下邏輯:

forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

???????會遍歷WindowState執(zhí)行:

mService.mPolicy.layoutWindowLw(w, null);

???????執(zhí)行到PhoneWindowManager的layoutWindowLw()方法尘奏,一起看一下:

public void layoutWindowLw(WindowState win, WindowState attached) {
    //StatusBar和NavigationBar在beginLayoutLw()內(nèi)已經(jīng)處理了 
    if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
        return;
    }
    final WindowManager.LayoutParams attrs = win.getAttrs();
    ................
    if (attrs.type == TYPE_INPUT_METHOD) {
    } else if (attrs.type == TYPE_WALLPAPER) {
        layoutWallpaper(win, pf, df, of, cf);
    } else {
       // Default policy decor for the default display
        dcf.left = mSystemLeft;
        dcf.top = mSystemTop;
        dcf.right = mSystemRight;
        dcf.bottom = mSystemBottom;
        final boolean isAppWindow =
                attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
                attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
       ................
    }
    ....................
    win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf)
    .......................
}

???????循環(huán)遍歷WindowState設置顯示大小及位置;
???????窗口顯示Layer已經(jīng)分配好病蛉,大小和位置也已經(jīng)設置好炫加,接下來就輪到顯示了,顯示流程請參考下篇文章Android WMS窗口管理(二)铺然;

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俗孝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子魄健,更是在濱河造成了極大的恐慌赋铝,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诀艰,死亡現(xiàn)場離奇詭異柬甥,居然都是意外死亡,警方通過查閱死者的電腦和手機其垄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卤橄,“玉大人绿满,你說我怎么就攤上這事】咂耍” “怎么了喇颁?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嚎货。 經(jīng)常有香客問我橘霎,道長,這世上最難降的妖魔是什么殖属? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任姐叁,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘外潜。我一直安慰自己原环,他們只是感情好,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布处窥。 她就那樣靜靜地躺著嘱吗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滔驾。 梳的紋絲不亂的頭發(fā)上谒麦,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機與錄音哆致,去河邊找鬼绕德。 笑死,一個胖子當著我的面吹牛沽瞭,可吹牛的內(nèi)容都是我干的迁匠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼驹溃,長吁一口氣:“原來是場噩夢啊……” “哼城丧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起豌鹤,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤亡哄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后布疙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚊惯,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年灵临,在試婚紗的時候發(fā)現(xiàn)自己被綠了截型。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡儒溉,死狀恐怖宦焦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情顿涣,我是刑警寧澤波闹,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站涛碑,受9級特大地震影響精堕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蒲障,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一歹篓、第九天 我趴在偏房一處隱蔽的房頂上張望瘫证。 院中可真熱鬧,春花似錦滋捶、人聲如沸痛悯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽载萌。三九已至,卻和暖如春巡扇,著一層夾襖步出監(jiān)牢的瞬間扭仁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工厅翔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乖坠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓刀闷,卻偏偏與公主長得像熊泵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子甸昏,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容