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

這篇博客我們主要分析下,窗口位置排序的一些原理。


一、添加窗口的時候 調整窗口位置

上篇博客我們分析了WMS的addWindow函數,這裏我們就窗口的次序問題繼續分析。

  1. boolean imMayMove = true;
  2. if (type == TYPE_INPUT_METHOD) {//如果窗口類是輸入法窗口
  3. win.mGivenInsetsPending = true;
  4. mInputMethodWindow = win;
  5. addInputMethodWindowToListLocked(win);//插入輸入法窗口到應用窗口上層
  6. imMayMove = false;
  7. } else if (type == TYPE_INPUT_METHOD_DIALOG) {//如果窗口是輸入法對話框
  8. mInputMethodDialogs.add(win);
  9. addWindowToListInOrderLocked(win, true);//插入到正常位置
  10. moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));//調整對話框位置
  11. imMayMove = false;
  12. } else {
  13. addWindowToListInOrderLocked(win, true);//插入正常位置
  14. if (type == TYPE_WALLPAPER) {
  15. mLastWallpaperTimeoutTime = 0;
  16. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  17. } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
  18. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  19. } else if (mWallpaperTarget != null
  20. && mWallpaperTarget.mLayer >= win.mBaseLayer) {
  21. // If there is currently a wallpaper being shown, and
  22. // the base layer of the new window is below the current
  23. // layer of the target window, then adjust the wallpaper.
  24. // This is to avoid a new window being placed between the
  25. // wallpaper and its target.
  26. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  27. }
  28. }

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

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

addWindowToListInOrderLocked將一個窗口插入到窗口堆棧的當前位置。


我們繼續看addWindow函數,

  1. final WindowStateAnimator winAnimator = win.mWinAnimator;
  2. winAnimator.mEnterAnimationPending = true;
  3. winAnimator.mEnteringAnimation = true;
  4. if (displayContent.isDefaultDisplay) {
  5. mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,//計算窗口大小
  6. outOutsets);
  7. } else {
  8. outContentInsets.setEmpty();
  9. outStableInsets.setEmpty();
  10. }
  11. if (mInTouchMode) {
  12. res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;//加入支持觸屏的標誌
  13. }
  14. if (win.mAppToken == null || !win.mAppToken.clientHidden) {
  15. res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;//加入應用可用的標誌
  16. }
  17. mInputMonitor.setUpdateInputWindowsNeededLw();//設置更新輸入法窗口的標誌
  18. boolean focusChanged = false;
  19. if (win.canReceiveKeys()) {//如果窗口能接受輸入計算是否引起焦點變化
  20. focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
  21. false /*updateInputWindows*/);
  22. if (focusChanged) {
  23. imMayMove = false;
  24. }
  25. }
  26. if (imMayMove) {
  27. moveInputMethodWindowsIfNeededLocked(false);//調整輸入法的窗口位置
  28. }
  29. assignLayersLocked(displayContent.getWindowList());//重新計算z軸的位置
  30. // Don't do layout here, the window must call
  31. // relayout to be displayed, so we'll do it there.
  32. if (focusChanged) {
  33. mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
  34. }
  35. mInputMonitor.updateInputWindowsLw(false /*force*/);//更新輸入法窗口的信息
  36. if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client "
  37. + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
  38. if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
  39. reportNewConfig = true;//配置發生變化
  40. }
  41. }
  42. if (reportNewConfig) {
  43. sendNewConfiguration();//發送新的配置
  44. }
  45. Binder.restoreCallingIdentity(origId);
  46. return res;
  47. }

如果窗口顯示在缺省的顯示設備,調用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的構造函數中有如下代碼:

  1. if ((mAttrs.type >= FIRST_SUB_WINDOW &&
  2. mAttrs.type <= LAST_SUB_WINDOW)) {//如果是子窗口
  3. // The multiplier here is to reserve space for multiple
  4. // windows in the same type layer.
  5. mBaseLayer = mPolicy.windowTypeToLayerLw(//使用依附窗口的類型
  6. attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
  7. + WindowManagerService.TYPE_LAYER_OFFSET;
  8. mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);//計算mSubLayer
  9. ......
  10. } else {//非子窗口
  11. // The multiplier here is to reserve space for multiple
  12. // windows in the same type layer.
  13. mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
  14. * WindowManagerService.TYPE_LAYER_MULTIPLIER
  15. + WindowManagerService.TYPE_LAYER_OFFSET;
  16. mSubLayer = 0;
  17. ......
  18. }

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

  1. public int windowTypeToLayerLw(int type) {
  2. if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {//應用窗口
  3. return 2;
  4. }
  5. switch (type) {
  6. case TYPE_PRIVATE_PRESENTATION:
  7. return 2;
  8. case TYPE_WALLPAPER:
  9. // wallpaper is at the bottom, though the window manager may move it.
  10. return 2;
  11. case TYPE_PHONE:
  12. return 3;
  13. case TYPE_SEARCH_BAR:
  14. case TYPE_VOICE_INTERACTION_STARTING:
  15. return 4;
  16. case TYPE_VOICE_INTERACTION:
  17. // voice interaction layer is almost immediately above apps.
  18. return 5;
  19. case TYPE_INPUT_CONSUMER:
  20. return 6;
  21. case TYPE_SYSTEM_DIALOG:
  22. return 7;
  23. case TYPE_TOAST:
  24. // toasts and the plugged-in battery thing
  25. return 8;
  26. case TYPE_PRIORITY_PHONE:
  27. // SIM errors and unlock. Not sure if this really should be in a high layer.
  28. return 9;
  29. case TYPE_DREAM:
  30. // used for Dreams (screensavers with TYPE_DREAM windows)
  31. return 10;
  32. case TYPE_SYSTEM_ALERT:
  33. // like the ANR / app crashed dialogs
  34. return 11;
  35. case TYPE_INPUT_METHOD:
  36. // on-screen keyboards and other such input method user interfaces go here.
  37. return 12;
  38. case TYPE_INPUT_METHOD_DIALOG:
  39. // on-screen keyboards and other such input method user interfaces go here.
  40. return 13;
  41. case TYPE_KEYGUARD_SCRIM:
  42. // the safety window that shows behind keyguard while keyguard is starting
  43. return 14;
  44. case TYPE_STATUS_BAR_SUB_PANEL:
  45. return 15;
  46. case TYPE_STATUS_BAR:
  47. return 16;
  48. case TYPE_STATUS_BAR_PANEL:
  49. return 17;
  50. case TYPE_KEYGUARD_DIALOG:
  51. return 18;
  52. case TYPE_VOLUME_OVERLAY:
  53. // the on-screen volume indicator and controller shown when the user
  54. // changes the device volume
  55. return 19;
  56. case TYPE_SYSTEM_OVERLAY:
  57. // the on-screen volume indicator and controller shown when the user
  58. // changes the device volume
  59. return 20;
  60. case TYPE_NAVIGATION_BAR:
  61. // the navigation bar, if available, shows atop most things
  62. return 21;
  63. case TYPE_NAVIGATION_BAR_PANEL:
  64. // some panels (e.g. search) need to show on top of the navigation bar
  65. return 22;
  66. case TYPE_SYSTEM_ERROR:
  67. // system-level error dialogs
  68. return 23;
  69. case TYPE_MAGNIFICATION_OVERLAY:
  70. // used to highlight the magnified portion of a display
  71. return 24;
  72. case TYPE_DISPLAY_OVERLAY:
  73. // used to simulate secondary display devices
  74. return 25;
  75. case TYPE_DRAG:
  76. // the drag layer: input for drag-and-drop is associated with this window,
  77. // which sits above all other focusable windows
  78. return 26;
  79. case TYPE_ACCESSIBILITY_OVERLAY:
  80. // overlay put by accessibility services to intercept user interaction
  81. return 27;
  82. case TYPE_SECURE_SYSTEM_OVERLAY:
  83. return 28;
  84. case TYPE_BOOT_PROGRESS:
  85. return 29;
  86. case TYPE_POINTER:
  87. // the (mouse) pointer layer
  88. return 30;
  89. }
  90. Log.e(TAG, "Unknown window type: " + type);
  91. return 2;
  92. }

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

WindowState中的成員變量mSubLayer只有在窗口是子窗口的時候纔有作用,它表示在窗口和父窗口之間的相對位置。代碼如下

  1. public int subWindowTypeToLayerLw(int type) {
  2. switch (type) {
  3. case TYPE_APPLICATION_PANEL:
  4. case TYPE_APPLICATION_ATTACHED_DIALOG:
  5. return APPLICATION_PANEL_SUBLAYER;//等於1
  6. case TYPE_APPLICATION_MEDIA:
  7. return APPLICATION_MEDIA_SUBLAYER;//等於-2
  8. case TYPE_APPLICATION_MEDIA_OVERLAY:
  9. return APPLICATION_MEDIA_OVERLAY_SUBLAYER;//等於-1
  10. case TYPE_APPLICATION_SUB_PANEL:
  11. return APPLICATION_SUB_PANEL_SUBLAYER;//等於2
  12. case TYPE_APPLICATION_ABOVE_SUB_PANEL:
  13. return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;//等於3
  14. }
  15. Log.e(TAG, "Unknown sub-window type: " + type);
  16. return 0;
  17. }

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

  1. private final void assignLayersLocked(WindowList windows) {
  2. int N = windows.size();
  3. int curBaseLayer = 0;
  4. int curLayer = 0;
  5. int i;
  6. boolean anyLayerChanged = false;
  7. for (i=0; i<N; i++) {
  8. final WindowState w = windows.get(i);
  9. final WindowStateAnimator winAnimator = w.mWinAnimator;
  10. boolean layerChanged = false;
  11. int oldLayer = w.mLayer;
  12. if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
  13. || (i > 0 && w.mIsWallpaper)) {//如果窗口的mBaseLayer和前一個相同、或者是輸入法和壁紙窗口
  14. curLayer += WINDOW_LAYER_MULTIPLIER;
  15. w.mLayer = curLayer;
  16. } else {
  17. curBaseLayer = curLayer = w.mBaseLayer;
  18. w.mLayer = curLayer;
  19. }
  20. if (w.mLayer != oldLayer) {//層級發生改變
  21. layerChanged = true;
  22. anyLayerChanged = true;
  23. }
  24. final AppWindowToken wtoken = w.mAppToken;
  25. oldLayer = winAnimator.mAnimLayer;//後面都是調整mAnimLayerd的值
  26. if (w.mTargetAppToken != null) {
  27. winAnimator.mAnimLayer =
  28. w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
  29. } else if (wtoken != null) {
  30. winAnimator.mAnimLayer =
  31. w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
  32. } else {
  33. winAnimator.mAnimLayer = w.mLayer;
  34. }
  35. if (w.mIsImWindow) {
  36. winAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
  37. } else if (w.mIsWallpaper) {
  38. winAnimator.mAnimLayer += mWallpaperAnimLayerAdjustment;
  39. }
  40. if (winAnimator.mAnimLayer != oldLayer) {
  41. layerChanged = true;
  42. anyLayerChanged = true;
  43. }
  44. final TaskStack stack = w.getStack();
  45. if (layerChanged && stack != null && stack.isDimming(winAnimator)) {
  46. // Force an animation pass just to update the mDimLayer layer.
  47. scheduleAnimationLocked();
  48. }
  49. }
  50. if (mAccessibilityController != null && anyLayerChanged
  51. && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
  52. mAccessibilityController.onWindowLayersChangedLocked();
  53. }
  54. }

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

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

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

三、插入窗口的位置

在addWindow函數中我們會調用addAppWindowToListLocked來確定窗口的位置,現在我們來看下這個函數。

  1. private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
  2. if (win.mAttachedWindow == null) {//非子窗口
  3. final WindowToken token = win.mToken;
  4. int tokenWindowsPos = 0;
  5. if (token.appWindowToken != null) {//應用窗口的頂層窗口
  6. tokenWindowsPos = addAppWindowToListLocked(win);
  7. } else {
  8. addFreeWindowToListLocked(win);//系統窗口
  9. }
  10. if (addToToken) {
  11. if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
  12. token.windows.add(tokenWindowsPos, win);
  13. }
  14. } else {
  15. addAttachedWindowToListLocked(win, addToToken);//添加子窗口
  16. }
  17. if (win.mAppToken != null && addToToken) {
  18. win.mAppToken.allAppWindows.add(win);
  19. }
  20. }

上面這個函數根據窗口的類型,應用頂層窗口,系統窗口,子窗口。

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

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

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

下面是部分代碼,

  1. private int addAppWindowToListLocked(final WindowState win) {
  2. final IWindow client = win.mClient;
  3. final WindowToken token = win.mToken;
  4. final DisplayContent displayContent = win.getDisplayContent();
  5. if (displayContent == null) {
  6. // It doesn't matter this display is going away.
  7. return 0;
  8. }
  9. final WindowList windows = win.getWindowList();
  10. final int N = windows.size();
  11. WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
  12. int tokenWindowsPos = 0;
  13. int windowListPos = tokenWindowList.size();
  14. if (!tokenWindowList.isEmpty()) {//如果有,說明它不是第一個窗口
  15. // If this application has existing windows, we
  16. // simply place the new window on top of them... but
  17. // keep the starting window on top.
  18. if (win.mAttrs.type == TYPE_BASE_APPLICATION) {//放在和它相同Token的窗口下面
  19. // Base windows go behind everything else.
  20. WindowState lowestWindow = tokenWindowList.get(0);//第一個0,代表最底層的window
  21. placeWindowBefore(lowestWindow, win);//放在這個window前面
  22. tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
  23. } else {
  24. AppWindowToken atoken = win.mAppToken;
  25. WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
  26. if (atoken != null && lastWindow == atoken.startingWindow) {
  27. placeWindowBefore(lastWindow, win);
  28. tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
  29. } else {
  30. int newIdx = findIdxBasedOnAppTokens(win);//尋找同一token位置最前面的window
  31. //there is a window above this one associated with the same
  32. //apptoken note that the window could be a floating window
  33. //that was created later or a window at the top of the list of
  34. //windows associated with this token.
  35. windows.add(newIdx + 1, win);//插在它前面
  36. if (newIdx < 0) {
  37. // No window from token found on win's display.
  38. tokenWindowsPos = 0;
  39. } else {
  40. tokenWindowsPos = indexOfWinInWindowList(
  41. windows.get(newIdx), token.windows) + 1;
  42. }
  43. mWindowsChanged = true;
  44. }
  45. }
  46. return tokenWindowsPos;
  47. }

我們再來看幾個函數

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

  1. private void placeWindowBefore(WindowState pos, WindowState window) {
  2. final WindowList windows = pos.getWindowList();
  3. int i = windows.indexOf(pos);
  4. if (i < 0) {
  5. Slog.w(TAG, "placeWindowBefore: Unable to find " + pos + " in " + windows);
  6. i = 0;
  7. }
  8. windows.add(i, window);
  9. mWindowsChanged = true;
  10. }

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

  1. private int findIdxBasedOnAppTokens(WindowState win) {
  2. WindowList windows = win.getWindowList();
  3. for(int j = windows.size() - 1; j >= 0; j--) {
  4. WindowState wentry = windows.get(j);
  5. if(wentry.mAppToken == win.mAppToken) {
  6. return j;
  7. }
  8. }
  9. return -1;
  10. }


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

  1. WindowState pos = null;
  2. final ArrayList<Task> tasks = displayContent.getTasks();
  3. int taskNdx;
  4. int tokenNdx = -1;
  5. for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
  6. AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
  7. for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
  8. final AppWindowToken t = tokens.get(tokenNdx);
  9. if (t == token) {
  10. --tokenNdx;
  11. if (tokenNdx < 0) {
  12. --taskNdx;
  13. if (taskNdx >= 0) {
  14. tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1;
  15. }
  16. }
  17. break;
  18. }
  19. // We haven't reached the token yet; if this token
  20. // is not going to the bottom and has windows on this display, we can
  21. // use it as an anchor for when we do reach the token.
  22. tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
  23. if (!t.sendingToBottom && tokenWindowList.size() > 0) {
  24. pos = tokenWindowList.get(0);
  25. }
  26. }
  27. if (tokenNdx >= 0) {
  28. // early exit
  29. break;
  30. }
  31. }
  32. // We now know the index into the apps. If we found
  33. // an app window above, that gives us the position; else
  34. // we need to look some more.
  35. if (pos != null) {
  36. // Move behind any windows attached to this one.
  37. WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
  38. if (atoken != null) {
  39. tokenWindowList =
  40. getTokenWindowsOnDisplay(atoken, displayContent);
  41. final int NC = tokenWindowList.size();
  42. if (NC > 0) {
  43. WindowState bottom = tokenWindowList.get(0);
  44. if (bottom.mSubLayer < 0) {
  45. pos = bottom;
  46. }
  47. }
  48. }
  49. placeWindowBefore(pos, win);
  50. return tokenWindowsPos;
  51. }
  52. // Continue looking down until we find the first
  53. // token that has windows on this display.
  54. for ( ; taskNdx >= 0; --taskNdx) {
  55. AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
  56. for ( ; tokenNdx >= 0; --tokenNdx) {
  57. final AppWindowToken t = tokens.get(tokenNdx);
  58. tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
  59. final int NW = tokenWindowList.size();
  60. if (NW > 0) {
  61. pos = tokenWindowList.get(NW-1);
  62. break;
  63. }
  64. }
  65. if (tokenNdx >= 0) {
  66. // found
  67. break;
  68. }
  69. }
  70. if (pos != null) {
  71. // Move in front of any windows attached to this
  72. // one.
  73. WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
  74. if (atoken != null) {
  75. final int NC = atoken.windows.size();
  76. if (NC > 0) {
  77. WindowState top = atoken.windows.get(NC-1);
  78. if (top.mSubLayer >= 0) {
  79. pos = top;
  80. }
  81. }
  82. }
  83. placeWindowAfter(pos, win);
  84. return tokenWindowsPos;
  85. }
  86. ......

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

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


2. 插入子窗口的addAttachedWindowToListLocked函數

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

  1. private void addAttachedWindowToListLocked(final WindowState win, boolean addToToken) {
  2. final WindowToken token = win.mToken;
  3. final DisplayContent displayContent = win.getDisplayContent();
  4. if (displayContent == null) {
  5. return;
  6. }
  7. final WindowState attached = win.mAttachedWindow;
  8. WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
  9. // Figure out this window's ordering relative to the window
  10. // it is attached to.
  11. final int NA = tokenWindowList.size();
  12. final int sublayer = win.mSubLayer;
  13. int largestSublayer = Integer.MIN_VALUE;
  14. WindowState windowWithLargestSublayer = null;
  15. int i;
  16. for (i = 0; i < NA; i++) {
  17. WindowState w = tokenWindowList.get(i);
  18. final int wSublayer = w.mSubLayer;
  19. if (wSublayer >= largestSublayer) {
  20. largestSublayer = wSublayer;
  21. windowWithLargestSublayer = w;
  22. }
  23. if (sublayer < 0) {
  24. // For negative sublayers, we go below all windows
  25. // in the same sublayer.
  26. if (wSublayer >= sublayer) {
  27. if (addToToken) {
  28. if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
  29. token.windows.add(i, win);
  30. }
  31. placeWindowBefore(wSublayer >= 0 ? attached : w, win);
  32. break;
  33. }
  34. } else {
  35. // For positive sublayers, we go above all windows
  36. // in the same sublayer.
  37. if (wSublayer > sublayer) {
  38. if (addToToken) {
  39. if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
  40. token.windows.add(i, win);
  41. }
  42. placeWindowBefore(w, win);
  43. break;
  44. }
  45. }
  46. }
  47. if (i >= NA) {
  48. if (addToToken) {
  49. if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
  50. token.windows.add(win);
  51. }
  52. if (sublayer < 0) {
  53. placeWindowBefore(attached, win);
  54. } else {
  55. placeWindowAfter(largestSublayer >= 0
  56. ? windowWithLargestSublayer
  57. : attached,
  58. win);
  59. }
  60. }
  61. }


3.插入系統窗口的addFreeWindowToListLocked

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

  1. private void addFreeWindowToListLocked(final WindowState win) {
  2. final WindowList windows = win.getWindowList();
  3. // Figure out where window should go, based on layer.
  4. final int myLayer = win.mBaseLayer;
  5. int i;
  6. for (i = windows.size() - 1; i >= 0; i--) {
  7. if (windows.get(i).mBaseLayer <= myLayer) {
  8. break;
  9. }
  10. }
  11. i++;
  12. if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
  13. "Free window: Adding window " + win + " at " + i + " of " + windows.size());
  14. windows.add(i, win);
  15. mWindowsChanged = true;
  16. }



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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章