原文出處——>[Android窗口管理服務WindowManagerService計算窗口Z軸位置的過程分析](http://blog.csdn.net/luoshengyang/article/details/8570428)
通過前面幾篇文章的學習,我們知道了在Android系統中,無論是普通的Activity窗口,還是特殊的輸入法窗口和壁紙窗口,它們都是被WindowManagerService服務組織在一個窗口堆棧中的,其中,Z軸位置較大的窗口排列在Z軸位置較小的窗口的上面。有了這個窗口堆棧之后,WindowManagerService服務就可以按照一定的規則計算每一個窗口的Z軸位置了,本文就詳細分析這個計算過程。
基于窗口堆棧來計算窗口的Z軸位置是比較有意思的。按照一般的理解,應該是先計算好窗口的Z軸位置,然后再按照Z軸位置的大小來將各個窗口排列在堆棧中。但是,事實上,窗口是按照其它規則排列在堆棧中。這些規則與窗口的類型、創建順序和運行狀態等有關。例如,狀態欄窗口總是位于堆棧的頂端,輸入法窗口總是位于需要輸入法的窗口的上面,而壁紙窗口總是位于需要顯示壁紙的窗口的下面。又如,當一個Activity組件從后臺激活到前臺時,與它所對應的窗口就會被相應地移動到窗口堆棧的上面去。
從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃和Android系統Surface機制的SurfaceFlinger服務簡要介紹和學習計劃這兩個系列的文章可以知道,窗口的UI最終是需要通過SurfaceFlinger服務來統一渲染的,而SurfaceFlinger服務在渲染窗口的UI之前,需要計算基于各個窗口的Z軸位置來計算它們的可見區域。因此,WindowManagerService服務計算好每一個窗口的Z軸位置之后,還需要將它們設置到SurfaceFlinger服務中去,以便SurfaceFlinger服務可以正確地渲染每一個窗口的UI。
上述窗口的Z軸位置計算和設置過程如圖1所示:
:-: 
圖1 窗口Z軸位置的計算和設置過程
接下來,我們就首先分析兩個需要重新計算窗口Z軸位置的情景,接著再分析窗口的Z軸位置的計算過程,最后分析WindowManagerService服務將窗口的Z軸設置到SurfaceFlinger服務中去的過程。
#### **一. 需要重新計算窗口Z軸位置的情景**
這里主要分析兩個需要重新計算窗口Z軸位置的情景:應用程序增加一個窗口到WindowManagerService服務和應用程序請求WindowManagerService服務重新布局一個窗口。
從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文可以知道,應用程序請求增加一個窗口到WindowManagerService服務的時候,最終會調用到WindowManagerService類的成員函數addWindow。接下來我們就主要分析這個函數與重新計算窗口Z軸位置相關的邏輯,如下所示:
~~~
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets, InputChannel outInputChannel) {
......
synchronized(mWindowMap) {
......
WindowToken token = mTokenMap.get(attrs.token);
......
win = new WindowState(session, client, token,
attachedWindow, attrs, viewVisibility);
......
if (attrs.type == TYPE_INPUT_METHOD) {
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);
......
} else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
adjustInputMethodDialogsLocked();
......
} else {
addWindowToListInOrderLocked(win, true);
if (attrs.type == TYPE_WALLPAPER) {
......
adjustWallpaperWindowsLocked();
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
}
......
assignLayersLocked();
......
}
......
}
......
}
~~~
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類的成員函數addWindow的具體實現可以參考Android窗口管理服務WindowManagerService對壁紙窗口(Wallpaper Window)的管理分析和Android窗口管理服務WindowManagerService對輸入法窗口(Input Method Window)的管理分析這兩篇文章。我們注意到,WindowManagerService類的成員函數addWindow會根據當前正在添加的窗口的類型來調用不同的成員函數來向窗口堆棧的合適位置插入一個WindowState對象,即:
1. 如果添加的是一個輸入法窗口,那么就調用成員函數addInputMethodWindowToListLocked將它放置在需要顯示輸入法的窗口的上面去;
2. 如果添加的是一個輸入法對話框,那么就先調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中,接著再調用成員函數adjustInputMethodDialogsLocked來將它放置在輸入法窗口的上面;
3. 如果添加的是一個普通窗口,那么就直接調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中;
4. 如果添加的是一個普通窗口,并且這個窗口需要顯示壁紙,那么就先調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中,接著再調用成員函數adjustWallpaperWindowsLocked來將壁紙窗口放置在它的下面。
5. 如果添加的是一個壁紙窗口,那么就先調用成員函數addWindowToListInOrderLocked來將它插入到窗口堆棧中,接著再調用成員函數adjustWallpaperWindowsLocked來將它放置在需要顯示壁紙的窗口的下面。
無論如何,WindowManagerService類的成員函數addWindow最終都會調用成員函數assignLayersLocked來重新計算系統中所有窗口的Z軸位置,這是因為前面往窗口堆棧增加了一個新的窗口。
從前面Android窗口管理服務WindowManagerService計算Activity窗口大小的過程分析一文可以知道,應用程序進程請求WindowManagerService服務重新布局一個窗口的時候,最終會調用到WindowManagerService類的成員函數relayoutWindow。接下來我們就主要分析這個函數與重新計算窗口Z軸位置相關的邏輯,如下所示:
~~~
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
Configuration outConfig, Surface outSurface) {
......
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
......
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
}
......
boolean imMayMove = (flagChanges&(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
......
if (focusMayChange) {
......
if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
imMayMove = false;
}
......
}
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean assignLayers = false;
if (imMayMove) {
if (moveInputMethodWindowsIfNeededLocked(false) || displayed) {
// Little hack here -- we -should- be able to rely on the
// function to return true if the IME has moved and needs
// its layer recomputed. However, if the IME was hidden
// and isn't actually moved in the list, its layer may be
// out of data so we make sure to recompute it.
assignLayers = true;
}
}
if (wallpaperMayMove) {
if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
assignLayers = true;
}
}
......
if (assignLayers) {
assignLayersLocked();
}
......
}
......
return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
| (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
}
......
}
~~~
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類的成員函數relayoutWindow具體實現可以參考Android窗口管理服務WindowManagerService對壁紙窗口(Wallpaper Window)的管理分析和Android窗口管理服務WindowManagerService對輸入法窗口(Input Method Window)的管理分析這兩篇文章,與窗口Z軸位置計算相關的邏輯大概是這樣的:
1. 如果系統當前獲得焦點的窗口可能發生了變化,那么就會調用成員函數updateFocusedWindowLocked來重新計算系統當前應該獲得焦點的窗口。如果系統當前獲得焦點的窗口真的發生了變化,即窗口堆棧的窗口排列發生了變化,那么在調用成員函數updateFocusedWindowLocked的時候,就會調用成員函數assignLayersLocked來重新計算系統中所有窗口的Z軸位置。
2. 如果系統中的輸入法窗口可能需要移動,那么就會調用成員函數moveInputMethodWindowsIfNeededLocked來檢查是否真的需要移動輸入法窗口。如果需要移動,那么成員函數moveInputMethodWindowsIfNeededLocked的返回值就會等于true,這時候就說明輸入法窗口在窗口堆棧中的位置發生了變化,因此,就會將變量assignLayers的值設置為true,表示接下來需要重新計算系統中所有窗口的Z軸位置。
3. 如果當前正在請求調整其布局的窗口是由不可見變化可見的,即變量displayed的值等于true,那么接下來也是需要重新計算系統中所有窗口的Z軸位置的,因此,就會將assignLayers的值設置為true。
4. 如果系統中的壁紙窗口可能需要移動,那么就會調用成員函數adjustWallpaperWindowsLocked來檢查是否真的需要移動壁紙窗口。如果需要移動,那么成員函數adjustWallpaperWindowsLocked的返回值的ADJUST_WALLPAPER_LAYERS_CHANGED位就會等于1,這時候就說明壁紙窗口在窗口堆棧中的位置發生了變化,因此,就會將變量assignLayers的值設置為true,表示接下來需要重新計算系統中所有窗口的Z軸位置。
經過上述的一系列操作后,如果得到的變量assignLayers的值設置等于true,那么WindowManagerService類的成員函數relayoutWindow就會調用成員函數assignLayersLocked來重新計算系統中所有窗口的Z軸位置。
#### **二. 計算系統中所有窗口的Z軸位置**
從前面第一部分的內容可以知道,一旦窗口堆棧中的窗口發生了變化,那么WindowManagerService類的成員函數assignLayersLocked就會調用來計算系統中所有窗口的Z軸位置。
窗口的Z軸位置除了與它在窗口堆棧中的位置有關之外,還與窗口的類型有關。窗口的類型在創建的時候就已經是確定了的,WindowManagerService服務在為它創建一個WindowState對象的時候,就會根據它的類型得到一個BaseLayer值,這個BaseLayer值在計算它的Z軸位置的時候會用到。
接下來我們就通過WindowState類的構造函數來分析一個窗口的BaseLayer值是如何確定的,如下所示:
~~~
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
* with TYPE_LAYER_OFFSET. */
static final int TYPE_LAYER_MULTIPLIER = 10000;
/** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
* or below others in the same layer. */
static final int TYPE_LAYER_OFFSET = 1000;
......
private final class WindowState implements WindowManagerPolicy.WindowState {
......
final int mBaseLayer;
final int mSubLayer;
......
WindowState(Session s, IWindow c, WindowToken token,
WindowState attachedWindow, WindowManager.LayoutParams a,
int viewVisibility) {
......
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) * TYPE_LAYER_MULTIPLIER
+ TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
......
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
* TYPE_LAYER_MULTIPLIER
+ TYPE_LAYER_OFFSET;
mSubLayer = 0;
......
}
......
}
......
}
......
}
~~~
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
一個窗口除了有一個BaseLayer值之外,還有一個SubLayer值,分別保存在一個對應的WindowState對象的成員變量mBaseLayer和mSubLayer。SubLayer值是用來描述一個窗口是否是另外一個窗口的子窗口的。
假設一個窗口是另外一個窗口的子窗口,那么參數attachedWindow所描述的窗口就是父窗口,這時候子窗口的BaseLayer值就等于父窗口的BaseLayer值,而SubLayer值要么大于0,要么小于0,這與它自己的具體類型有關。
假設一個窗口不是另外一個窗口的子窗口,那么這個窗口的BaseLayer值就與它自己的具體類型有關,而SubLayer值就等于0。
現在的關鍵就是要根據窗口的類型來計算它的BaseLayer值和SubLayer值,它們分別是通過調用WindowManagerService類的成員變量mPolicy所指向的一個PhoneWindowManager對象的成員函數windowTypeToLayerLw和subWindowTypeToLayerLw來計算得到的。這里有兩個地方是需要注意的。
第一個地方是PhoneWindowManager對象的成員函數windowTypeToLayerLw的返回值并且不是一個窗口的最終的BaseLayer值,而是要將它的返回值乘以一個常量TYPE_LAYER_MULTIPLIER,再加上另外一個常量TYPE_LAYER_OFFSET之后,才得到最終的BaseLayer值。這是因為在Android系統中,相同類型的窗口的Z軸位置都是有著相同的值域,而不同類型的窗口的Z軸位置都是處于兩個不相交的值域。例如,假設有兩種不同類型的窗口,它們的Z軸位置的值域分別為[a, b]和[c, d],那么[a, b]和[c, d]的交集一定等于空。又由于每一種類型的窗口的數量是不確定的,因此,WindowManagerService服務就需要為每一種類型的窗口都預留一個范圍足夠大的值域,以便可以滿足要求。
WindowManagerService服務是如何為類型相同的窗口的Z軸位置預留一個范圍足夠大的值域的呢?我們假設類型為t的窗口的Z軸位置的值域為[a, b],并且以t為參數調用PhoneWindowManager對象的成員函數windowTypeToLayerLw的返回值為T,那么a的值就等于T * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET,而b的值就等于(T - 1) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET - 1,即從T * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET開始,一共預留了TYPE_LAYER_MULTIPLIER個值作為類型為t窗口的Z軸位置。由于TYPE_LAYER_MULTIPLIER的值定義為10000,而TYPE_LAYER_OFFSET的值定義為1000,因此,每一種類型的窗口都預留有一個足夠大的值域來作為Z軸位置。
第二個地方是窗口的SubLayer值并不直接參與窗口的Z軸位置的計算,但是它會影響到窗口在窗口堆棧的位置。接下來我們就會看到,窗口在窗口堆棧的位置是會影響到它的Z軸位置的計算的,因此,窗口的SubLayer間接地參與了窗口的Z軸位置的計算。
窗口的SubLayer值是如何影響到窗口在窗口堆棧的位置的呢?在前面Android窗口管理服務WindowManagerService對窗口的組織方式分析一文中,在分析WindowManagerService類的成員函數addWindowToListInOrderLocked的實現時提到,如果一個窗口是另外一個窗口的子窗口,那么當它的SubLayer值小于0的時候,它就會位于父窗口的下面,否則的話,就會位于父窗口的上面。
在繼續分析WindowManagerService類的成員函數assignLayersLocked之前,我們首先分析PhoneWindowManager類的成員函數windowTypeToLayerLw和subWindowTypeToLayerLw的實現,以便可以了解一個窗口的BaseLayer值和SubLayer值是如何確定的。
PhoneWindowManager類的成員函數windowTypeToLayerLw的實現如下所示:
~~~
public class PhoneWindowManager implements WindowManagerPolicy {
......
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER;
}
switch (type) {
case TYPE_STATUS_BAR:
return STATUS_BAR_LAYER;
case TYPE_STATUS_BAR_PANEL:
return STATUS_BAR_PANEL_LAYER;
case TYPE_SYSTEM_DIALOG:
return SYSTEM_DIALOG_LAYER;
case TYPE_SEARCH_BAR:
return SEARCH_BAR_LAYER;
case TYPE_PHONE:
return PHONE_LAYER;
case TYPE_KEYGUARD:
return KEYGUARD_LAYER;
case TYPE_KEYGUARD_DIALOG:
return KEYGUARD_DIALOG_LAYER;
case TYPE_SYSTEM_ALERT:
return SYSTEM_ALERT_LAYER;
case TYPE_SYSTEM_ERROR:
return SYSTEM_ERROR_LAYER;
case TYPE_INPUT_METHOD:
return INPUT_METHOD_LAYER;
case TYPE_INPUT_METHOD_DIALOG:
return INPUT_METHOD_DIALOG_LAYER;
case TYPE_SYSTEM_OVERLAY:
return SYSTEM_OVERLAY_LAYER;
case TYPE_SECURE_SYSTEM_OVERLAY:
return SECURE_SYSTEM_OVERLAY_LAYER;
case TYPE_PRIORITY_PHONE:
return PRIORITY_PHONE_LAYER;
case TYPE_TOAST:
return TOAST_LAYER;
case TYPE_WALLPAPER:
return WALLPAPER_LAYER;
}
Log.e(TAG, "Unknown window type: " + type);
return APPLICATION_LAYER;
}
......
}
~~~
這個函數定義在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java文件中。
從這里就可以看出,每一種窗口類型type都對應有一個BaseLayer值,即每一個TYPE_XXX值都對應有一個XXX_LAYER值,其中,TYPE_XXX值定義在WindowManager.LayoutParams類中,而XXX_LAYER值就定義在PhoneWindowManager類中,它們的對應關系如圖2所示:
:-: 
圖2 窗口類型與窗口BaseLayer值的對應關系
注意,如果參數type的值小于FIRST_APPLICATION_WINDOW,或者大于LAST_APPLICATION_WINDOW,或者不是圖2列出來的其中一個值,那么PhoneWindowManager類的成員函數windowTypeToLayerLw就會返回一個APPLICATION_LAYER(2)值給調用者。
PhoneWindowManager類的成員函數subWindowTypeToLayerLw的實現如下所示:
~~~
public class PhoneWindowManager implements WindowManagerPolicy {
......
public int subWindowTypeToLayerLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
case TYPE_APPLICATION_ATTACHED_DIALOG:
return APPLICATION_PANEL_SUBLAYER;
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER;
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;
}
Log.e(TAG, "Unknown sub-window type: " + type);
return 0;
}
......
}
~~~
這個函數定義在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java文件中。
從這里就可以看出,只有類型為TYPE_APPLICATION_PANEL、TYPE_APPLICATION_MEDIA、TYPE_APPLICATION_MEDIA_OVERLAY和TYPE_APPLICATION_SUB_PANEL的窗口才對應有一個SubLayer值,它們的對應關系如圖3所示:
:-: 
圖3 窗口類型與窗口SubLayer值的對應關系
在圖3中,TYPE_XXX值定義在WindowManager.LayoutParams類中,而XXX_LAYER值就定義在PhoneWindowManager類中。注意,有兩種特殊的多媒體窗口TYPE_APPLICATION_MEDIA和TYPE_APPLICATION_MEDIA_OVERLAY,它們是用來顯示多媒體的,例如,用來顯示視頻,并且它們都是附加在應用程序窗口之上的,但是由于它們的SubLayer值為負數,因此它們實際上是位于宿主窗口之下的。類型為TYPE_APPLICATION_MEDIA的窗口有一個魔術,它會在宿主窗口里面挖一個洞,以便可以將自己顯示出來,而類型為TYPE_APPLICATION_MEDIA_OVERLAY背景一般都是透明的,位于類型為TYPE_APPLICATION_MEDIA的窗口,可以用來顯示視頻的字幕之類的東西。實際上,類型為TYPE_APPLICATION_MEDIA和TYPE_APPLICATION_MEDIA_OVERLAY的窗口也稱為SurfaceView。SurfaceView很特殊,它與普通的View的最大區別就在于它們有獨立的繪圖表面,于是它們就可以在一個獨立的子線程里面進行UI渲染。
理解了窗口的BaseLayer值和SubLayer值的計算過程之外,接下來我們就可以分析WindowManagerService類的成員函數assignLayersLocked的實現了,如下所示:
~~~
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
/** How much to increment the layer for each window, to reserve room
* for effect surfaces between them.
*/
static final int WINDOW_LAYER_MULTIPLIER = 5;
......
private final void assignLayersLocked() {
int N = mWindows.size();
int curBaseLayer = 0;
int curLayer = 0;
int i;
for (i=0; i<N; i++) {
WindowState w = mWindows.get(i);
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
|| (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
w.mLayer = curLayer;
}
if (w.mTargetAppToken != null) {
w.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
} else if (w.mAppToken != null) {
w.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
} else {
w.mAnimLayer = w.mLayer;
}
if (w.mIsImWindow) {
w.mAnimLayer += mInputMethodAnimLayerAdjustment;
} else if (w.mIsWallpaper) {
w.mAnimLayer += mWallpaperAnimLayerAdjustment;
}
......
}
}
......
}
~~~
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
注意,在調用WindowManagerService類的成員函數assignLayersLocked之前,系統中的所有窗口在窗口堆棧中的位置都是已經排列好了的,這時候WindowManagerService類的成員函數assignLayersLocked就從下往上遍歷窗口堆棧,以連排列在一起的類型相同的窗口為單位來計算每一個窗口的Z位置,即:
1. 每次遇到一個窗口,它的BaseLayer值與上一次計算的窗口的BaseLayer值不相等,就開始一個新的計算單元。
2. 在每一個計算單元中,第一個窗口的Z軸位置就等于它的BaseLayer值,而之后的每一個窗口的Z軸位置都比前一個窗口的Z軸位置大WINDOW_LAYER_MULTIPLIER。
這個窗口的Z軸位置計算方法有三個地方是需要注意的。
* 第一個地方是從第2點可以看出,每一個窗口的Z軸位置值都不是連續的,這樣就在每兩個窗口之間都保留了一定的位置來插入其它窗口。
* 第二個地方是由于系統中所有類型相同的窗口不一定都是排列在一起的,因此,就有可能出現有些類型相同的窗口具有相同的Z軸位置。WindowManagerService服務并不關心兩個不同窗口的Z軸位置是否相同,但是SurfaceFlinger服務就需要關心了,因為SurfaceFlinger服務需要是按照Z軸從大到小的順序來計算窗口的可見性。那么SurfaceFlinger服務是如何確定兩個Z軸位置相同的窗口的次序的呢?從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃和Android系統Surface機制的SurfaceFlinger服務簡要介紹和學習計劃這兩個系列的文章可以知道,每一個窗口在SurfaceFlinger服務都對應有一個Layer對象,而每一個Layer對象都有一個sequence值,其中,先創建的Layer對象的sequence值大于后創建的Layer對象的sequence值。這樣,SurfaceFlinger服務在計算對于兩個Z軸位置相同的窗口的可見性的時候,就會比較它們所對應的Layer對象的sequence值,其中,sequence值大的窗口的可見性會先于sequence值小的窗口得到計算,即先計算后創建的窗口的可見性,再計算先創建的窗口的可見性。
* 第三個地方是有兩種特殊的窗口,即輸入法窗口和壁紙窗口,當它們不是窗口堆棧底部的第一個窗口時,它們所在的計算單元不是以窗口類型來劃分的,而靠近在哪個窗口,就與哪個窗口在同一個計算單元中。當輸入法窗口是窗口堆棧底部的第一個窗口時,它的Z軸位置就等于WINDOW_LAYER_MULTIPLIER,而當壁紙窗口是窗口堆棧底部的第一個窗口時,它的Z軸位置就等于它的BaseLayer值。
前面計算得到的窗口的Z軸位置保存在WindowState類的成員變量mLayer中。事實上,保存在WindowState類的成員變量mLayer中的Z軸位置還不是窗口的最終Z軸位置,因為還沒有考慮到窗口與窗口令牌之間的關系。每一個窗口令牌都可以設置一個Z軸調整值,而每一個窗口要加上它所對應的窗口令牌所設置的Z軸調整值之后,才能得到最終的Z軸位置。注意,只有類型為AppWindowToken的窗口令牌才可以設置Z軸調整值,這個Z軸調整值就保存在AppWindowToken類的成員變量animLayerAdjustment中。
有時候,一個窗口會有一個目標窗口。例如,輸入法窗口的目標窗口是系統當前需要顯示輸入法的窗口。在這種情況下,我們要使用目標窗口所對應的窗口令牌所設置的Z軸調整值來調整窗口的的Z軸位置。
那么,WindowManagerService服務是如何知道一個窗口所對應的窗口令牌的類型是AppWindowToken,或者一個窗口有沒有目標窗口的呢?當用來描述一個窗口的WindowState對象成員變量mAppToken的值不等于null的時候,那么就說明該窗口所對應的窗口令牌的類型是AppWindowToken,而當用來描述一個窗口的WindowState對象成員變量mTargetAppToken的值不等于null的時候,那么就說明該窗口有一個目標窗口。
經過上面的調整之后,窗口的Z軸位置就保存在WindowState類的成員變量mAnimLayer中。對于非輸入法窗口和非壁紙窗口來說,這時候保存在用來描述它們的WindowState對象的成員變量mAnimLayer中的Z軸位置就是它們最終的Z軸位置了,但是對于輸入法窗口和壁紙窗口來說,還需要繼續判斷它們的目標窗口是否需要調整它們的Z軸位置。
從前面Android窗口管理服務WindowManagerService對壁紙窗口(Wallpaper Window)的管理分析和Android窗口管理服務WindowManagerService對輸入法窗口(Input Method Window)的管理分析這兩篇文章知道,如果一個窗口要調整它所關聯的輸入法窗口和壁紙窗口的Z軸位置,那么要調整的值就會保存在WindowManagerService類的成員變量mInputMethodAnimLayerAdjustment和mWallpaperAnimLayerAdjustment中,因此,只要將WindowManagerService類的成員變量mInputMethodAnimLayerAdjustment和mWallpaperAnimLayerAdjustment的值分別增加到前面所計算得到的輸入法窗口和壁紙窗口的Z軸位置上去,就可以得到輸入法窗口和壁紙窗口的最終Z軸位置,并且保存到用來對應的WindowState對象的成員變量mAnimLayer中。
從上面的計算過程就可以知道,系統中所有類型的窗口的最終Z軸位置都保存在WindowState類的成員變量mAnimLayer中。
#### **三. 設置窗口的Z軸位置到SurfaceFlinger服務中去**
WindowManagerService服務在刷新系統的UI的時候,就會將系統中已經計算好了的窗口Z軸位置設置到SurfaceFlinger服務中去,以便SurfaceFlinger服務可以對系統中的窗口進行可見性計算以及合成和渲染等操作。
從前面Android窗口管理服務WindowManagerService計算Activity窗口大小的過程分析一文可以知道,刷新系統UI是通過調用WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner來實現的,接下來我們就分析這個成員函數與設置窗口的Z軸位置到SurfaceFlinger服務中去相關的邏輯。
為了方便描述設置窗口的Z軸位置到SurfaceFlinger服務中去的過程,我們先列出WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner的實現架構,如下所示:
~~~
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
......
Surface.openTransaction();
......
try {
......
int repeats = 0;
int changes = 0;
do {
repeats++;
if (repeats > 6) {
......
break;
}
// FIRST LOOP: Perform a layout, if needed.
if (repeats < 4) {
changes = performLayoutLockedInner();
if (changes != 0) {
continue;
}
} else {
Slog.w(TAG, "Layout repeat skipped after too many iterations");
changes = 0;
}
// SECOND LOOP: Execute animations and update visibility of windows.
......
} while (changes != 0);
// THIRD LOOP: Update the surfaces of all windows.
......
//更新窗口的繪圖表面的操作包括:
//1. 設置窗口的大小
//2. 設置窗口在X軸和Y軸上的位置
//3. 設置窗口在Z軸上的位置
//4. 設置窗口的Alpha通道
//5. 設置窗口的變換矩陣
......
} catch (RuntimeException e) {
......
}
......
Surface.closeTransaction();
......
// Destroy the surface of any windows that are no longer visible.
......
// Time to remove any exiting tokens?
......
// Time to remove any exiting applications?
......
}
......
}
~~~
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
在前面Android窗口管理服務WindowManagerService計算Activity窗口大小的過程分析一文中,我們已經分析過WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner的實現架構了,其中,設置窗口的Z軸位置到SurfaceFlinger服務中去是在更新窗口的繪圖表面的操作中進行的,即是在THIRD LOOP中進行的,同時設置的還包括窗口的大小、X軸和Y軸位置、Alpha通道和變換矩陣,這些代碼如下所示:
~~~
//更新窗口的繪圖表面的操作包括:
//1. 設置窗口的大小
//2. 設置窗口在X軸和Y軸上的位置
//3. 設置窗口在Z軸上的位置
//4. 設置窗口的Alpha通道值
//5. 設置窗口的變換矩陣
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
WindowState w = mWindows.get(i);
......
if (w.mSurface != null) {
......
w.computeShownFrameLocked();
......
boolean resize;
int width, height;
if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
resize = w.mLastRequestedWidth != w.mRequestedWidth ||
w.mLastRequestedHeight != w.mRequestedHeight;
// for a scaled surface, we just want to use
// the requested size.
width = w.mRequestedWidth;
height = w.mRequestedHeight;
w.mLastRequestedWidth = width;
w.mLastRequestedHeight = height;
w.mLastShownFrame.set(w.mShownFrame);
try {
......
w.mSurfaceX = w.mShownFrame.left;
w.mSurfaceY = w.mShownFrame.top;
w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
} catch (RuntimeException e) {
......
if (!recoveringMemory) {
reclaimSomeSurfaceMemoryLocked(w, "position");
}
}
} else {
resize = !w.mLastShownFrame.equals(w.mShownFrame);
width = w.mShownFrame.width();
height = w.mShownFrame.height();
w.mLastShownFrame.set(w.mShownFrame);
}
if (resize) {
if (width < 1) width = 1;
if (height < 1) height = 1;
if (w.mSurface != null) {
try {
......
w.mSurfaceResized = true;
w.mSurfaceW = width;
w.mSurfaceH = height;
w.mSurface.setSize(width, height);
w.mSurfaceX = w.mShownFrame.left;
w.mSurfaceY = w.mShownFrame.top;
w.mSurface.setPosition(w.mShownFrame.left,
w.mShownFrame.top);
} catch (RuntimeException e) {
......
if (!recoveringMemory) {
reclaimSomeSurfaceMemoryLocked(w, "size");
}
}
}
}
......
if (w.mAttachedHidden || !w.isReadyForDisplay()) {
if (!w.mLastHidden) {
w.mLastHidden = true;
......
if (w.mSurface != null) {
w.mSurfaceShown = false;
try {
w.mSurface.hide();
} catch (RuntimeException e) {
......
}
}
}
......
} else if (w.mLastLayer != w.mAnimLayer
|| w.mLastAlpha != w.mShownAlpha
|| w.mLastDsDx != w.mDsDx
|| w.mLastDtDx != w.mDtDx
|| w.mLastDsDy != w.mDsDy
|| w.mLastDtDy != w.mDtDy
|| w.mLastHScale != w.mHScale
|| w.mLastVScale != w.mVScale
|| w.mLastHidden) {
......
w.mLastAlpha = w.mShownAlpha;
w.mLastLayer = w.mAnimLayer;
w.mLastDsDx = w.mDsDx;
w.mLastDtDx = w.mDtDx;
w.mLastDsDy = w.mDsDy;
w.mLastDtDy = w.mDtDy;
w.mLastHScale = w.mHScale;
w.mLastVScale = w.mVScale;
......
if (w.mSurface != null) {
try {
w.mSurfaceAlpha = w.mShownAlpha;
w.mSurface.setAlpha(w.mShownAlpha);
w.mSurfaceLayer = w.mAnimLayer;
w.mSurface.setLayer(w.mAnimLayer);
w.mSurface.setMatrix(
w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
} catch (RuntimeException e) {
.....
if (!recoveringMemory) {
reclaimSomeSurfaceMemoryLocked(w, "update");
}
}
}
if (w.mLastHidden && !w.mDrawPending
&& !w.mCommitDrawPending
&& !w.mReadyToShow) {
......
if (showSurfaceRobustlyLocked(w)) {
w.mHasDrawn = true;
w.mLastHidden = false;
}
}
......
}
......
}
~~~
這段代碼通過一個for循環來遍歷保存在窗口堆棧的每一個WindowState對象,以便可以對系統中的每一個窗口的繪圖表面進行更新。注意,只有那些成員變量mSurface的值不等于null的WindowState對象,它們所描述的窗口才具有繪圖表面,因此需要對它們進行更新。
在更新WindowState對象w所描述的窗口的繪圖表面之前,首先要調用它的成員函數computeShownFrameLocked來確定該窗口實際要顯示的大小、位置、Alpha通道和變換矩陣等信息,其中:
1. 窗口實際要顯示的大小和X軸、Y軸位置保存在WindowState對象w的成員變量mShownFrame中。
2. 窗口實際要顯示的Alpha通道保存在WindowState對象w的成員變量mShownAlpha中。
3. 窗口實際要顯示的Z軸位置保存在WindowState對象w的成員變量mAnimLayer中。
4. 窗口實際要使用的變換矩陣保存在WindowState對象w的成員變量mDsDx、mDtDx、mDsDy和mDtDy中。
有了上述信息之后,我們就可以將WindowState對象w所描述的窗口實際要顯示的大小、位置、Alpha通道和變換矩陣等信息設置到SurfaceFlinger服務中去了。
我們首先分析WindowState對象w所描述的窗口實際要顯示的大小、X軸和Y軸位置的設置過程,接著再分析WindowState對象w所描述的窗口實際要顯示的Alpha通道、Z軸位置以及實際要使用的變換矩陣的設置過程。
在調用WindowState對象w的成員函數computeShownFrameLocked來計算它所描述的窗口的大小的時候,是沒有考慮該窗口的大小是否設置有縮放因子的。
當WindowState對象w所描述的窗口的大小設置有縮放因子的時候,那么WindowState對象w的成員變量mAttrs所指向的一個WindowManager.LayoutParams對象的成員變量flags的FLAG_SCALED位就會等于1,這時候WindowState對象w所描述的窗口實際要顯示的大小是保存在它的成員變量mRequestedWidth和mRequestedHeight中的。在這種情況下,這段代碼就會執行以下操作:
1. 計算WindowState對象w所描述的窗口實際要顯示的大小是否發生了變化。如果發生了變化,那么就將變量resize的值設置為true。注意,WindowState對象w所描述的窗口上一次實際要顯示的大小保存在成員變量mLastRequestedWidth和mLastRequestedHeight中,因此,當這兩個成員變量與其它兩個成員變量mRequestedWidth和mRequestedHeight的值不相等于時,就說明WindowState對象w所描述的窗口實際要顯示的大小是否發生了變化。
2. 將WindowState對象w所描述的窗口實際要顯示的大小分別更新到成員變量mLastRequestedWidth和mLastRequestedHeight中,以及變量width和height中。
3. 將WindowState對象w的成員變量mShownFrame的值保存在另外一個成員變量mLastShownFrame中,以便可以記錄WindowState對象w所描述的窗口上一次實際要顯示的大小和X軸、Y軸位置。
4. 將WindowState對象w所描述的窗口的X軸和Y軸位置分別保存到成員變量mSurfaceX和mSurfaceY中,并且調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數setPosition來將這兩個位置值設置到SurfaceFlinger服務中去。
5. 在設置WindowState對象w所描述的窗口的X軸和Y軸位置到SurfaceFlinger服務中去的過程中,如果出現了異常,那么就說明系統內存資源不足。在這種情況下,如果參數recoveringMemory的值等于false,那么就說明WindowManagerService服務目前不是處于內存資源的回收過程中,于是就會調用WindowManagerService類的成員函數reclaimSomeSurfaceMemoryLocked來執行回收系統內存資源的操作。
當WindowState對象w所描述的窗口的大小沒有設置有縮放因子的時候,那么WindowState對象w的成員變量mAttrs所指向的一個WindowManager.LayoutParams對象的成員變量flags的FLAG_SCALED位就會等于0,這時候WindowState對象w所描述的窗口實際要顯示的大小是保存在它的成員變量mShownFrame中的。在這種情況下,這段代碼就會執行以下操作:
1. 計算WindowState對象w所描述的窗口實際要顯示的大小是否發生了變化。如果發生了變化,那么就將變量resize的值設置為true。注意,這時候只要比較WindowState對象w的成員變量mLastShownFrame和mShownFrame所描述的兩個矩形區域的大小是否相等,就可以知道WindowState對象w所描述的窗口實際要顯示的大小是否發生了變化,因為WindowState對象w的成員變量mLastShownFrame保存的是窗口上一次實際要顯示的大小。
2. 將WindowState對象w所描述的窗口實際要顯示的大小分別保存在變量width和height中。
3. 將WindowState對象w的成員變量mShownFrame的值保存在另外一個成員變量mLastShownFrame中,以便可以記錄WindowState對象w所描述的窗口上一次實際要顯示的大小和X軸、Y軸位置。
執行完成以上操作之后,WindowState對象w所描述的窗口實際要顯示的X軸和Y軸位置就保存在成員變量mShownFrame所描述的一個 Rect對象的成員變量left和top中,而實際要顯示的大小就顯示在變量width和height中。這時候如果變量resize的值等于true,那么就說明WindowState對象w所描述的窗口的大小發生了變化。在這種情況下,就需要執行以下操作:
1. 重新設置WindowState對象w所描述的窗口的大小到SurfaceFlinger服務中去,這是通過調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數setSize來實現的。注意,如果前面計算得到WindowState對象w所描述的窗口的寬度width和高度height的值小于1,那么就需要將它們的值設置為1,因為一個窗口的寬度和高度值是不能小于1的。
2. 重新設置WindowState對象w所描述的窗口在X軸和Y軸上的位置到SurfaceFlinger服務中去,這是通過調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數setPosition來實現的。注意,在設置之前,還會將WindowState對象w所描述的窗口在X軸和Y軸上的位置保存在成員變量mSurfaceX和mSurfaceY中。
3. 在設置WindowState對象w所描述的窗口的大小以及在X軸和Y軸上的位置到SurfaceFlinger服務中去的過程中,如果出現了異常,那么同樣需要判斷參數recoveringMemory的值來決定是否需要WindowManagerService類的成員函數reclaimSomeSurfaceMemoryLocked來回收系統內存資源。
設置好WindowState對象w所描述的窗口實際要顯示的大小、X軸和Y軸位置到SurfaceFlinger服務中去之后,接下來就要繼續設置它實際要顯示的Alpha通道、Z軸位置以及實際要使用的變換矩陣了,不過只有當WindowState對象w所描述的窗口當前是處于可見狀態、并且這些值沒有發生變化的情況下才需要這樣做。
當WindowState對象w的成員函數isReadyForDisplay的返回值等于false時,就說明WindowState對象w所描述的窗口當前是處于不可見狀態的。還有另外一種情況,即當WindowState對象w所描述的窗口是附加在另外一個窗口之上、并且這個被附加的窗口是不可見時,即WindowState對象w的成員變量mAttachedHidden的值等于true時,也是說明WindowState對象w所描述的窗口當前是處于不可見狀態的。
在WindowState對象w所描述的窗口當前是處于不可見狀態的情況下,如果該窗口在上一次系統UI刷新時是處于可見狀態的,即WindowState對象w的成員變量mLastHidden的值等于true,那么這時候就需要將WindowState對象w所描述的窗口隱藏起來,這是通過調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數hide來實現的。注意,在調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數hide來隱藏窗口之前,需要分別將WindowState對象w的成員變量mLastHidden和mSurfaceShown的值設置為true和false,以便可以正確描述窗口的不可見狀態。
在WindowState對象w所描述的窗口當前是處于可見狀態的情況下,如果該窗口實際要顯示的Alpha通道、Z軸位置以及實際要使用的變換矩陣發生了變化,那么就需要將新的值設置到SurfaceFlinger服務中去,其中:
1. WindowState對象w的成員變量mLastLayer與mAnimLayer的值不相等說明它描述的窗口的Z軸位置發生了變化。
2. WindowState對象w的成員變量mLastAlpha與mShownAlpha的值不相等說明它描述的窗口的Alpha通道發生了變化。
3. WindowState對象w的成員變量mLastDsDx、mLastDtDx、mLastDsDy、 mLastDtDy、mLastHScale、mLastVScale與成員變量mDsDx、mDtDx、mDsDy、 mDtDy、mHScale、mVScale的值不相等說明它描述的窗口的變換矩陣發生了變化。
在WindowState對象w所描述的窗口當前是處于可見狀態的情況下,如果該窗口在上一次系統UI刷新時是處于可見狀態的,即WindowState對象w的成員變量mLastHidden的值等于true,那么也是需要重新設置WindowState對象w所描述的窗口實際要顯示的Alpha通道、Z軸位置以及實際要使用的變換矩陣到SurfaceFlinger服務中去的。
無論如何,當需要重新設置WindowState對象w所描述的窗口實際要顯示的Alpha通道、Z軸位置以及實際要使用的變換矩陣到SurfaceFlinger服務中去時,就需要執行以下操作:
1. 重新設置WindowState對象w所描述的窗口的Alpha通道到SurfaceFlinger服務中去,這是通過調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數setAlpha來實現的。在設置之前,還會將WindowState對象w的成員變量mShownAlpha的值同時保存在成員變量mLastAlpha和mSurfaceAlpha中,以便可以記錄WindowState對象w所描述的窗口上一次所使用的Alpha通道。
2. 重新設置WindowState對象w所描述的窗口的Z軸位置到SurfaceFlinger服務中去,這是通過調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數setLayer來實現的。在設置之前,還會將WindowState對象w的成員變量mAnimLayer的值同時保存在成員變量mLastLayer和mSurfaceLayer中,以便可以記錄WindowState對象w所描述的窗口上一次所使用的Z軸位置。
3. 重新設置WindowState對象w所描述的窗口的變換矩陣到SurfaceFlinger服務中去,這是通過調用WindowState對象w的成員變量mSurface所指向的一個Surface對象的成員函數setMatrix來實現的。在設置之前,還會將WindowState對象w的成員變量成員變量mDsDx、mDtDx、mDsDy、 mDtDy、mHScale、mVScale的值分別保存在成員變量mLastDsDx、mLastDtDx、mLastDsDy、 mLastDtDy、mLastHScale、mLastVScale中,以便可以記錄WindowState對象w所描述的窗口上一次所使用的變換矩陣。注意,WindowState對象的成員變量mHScale和mVScale描述的窗口在寬度和高度上的縮放因子,因此,在設置窗口的變換矩陣時,需要乘以這些因子才可以得到正確的變換矩陣參數。
4. 在設置WindowState對象w所描述的窗口的Alpha通道、Z軸位置以及實際要使用的變換矩陣到SurfaceFlinger服務的過程中,如果出現了異常,那么同樣需要判斷參數recoveringMemory的值來決定是否需要WindowManagerService類的成員函數reclaimSomeSurfaceMemoryLocked來回收系統內存資源。
將WindowState對象w所描述的窗口實際要顯示的Alpha通道、Z軸位置以及實際要使用的變換矩陣設置到SurfaceFlinger服務之后,如果WindowState對象w所描述的窗口滿足以下條件:
1. 上一次處于不可見狀態,即WindowState對象w的成員變量mLastHidden的值等于true;
2. UI已經繪制完成,即WindowState對象w的成員變量mDrawPending和mCommitDrawPending值等于false;
3. 不是處于等待同一個窗口令牌的其它窗口的完成UI繪制的狀態,即WindowState對象w的成員變量mReadyToShow的值等于false;
那么就說明現在就是時候要將WindowState對象w所描述的窗口顯示出來了,這是通過調用WindowManagerService類的成員函數showSurfaceRobustlyLocked來實現的。如果WindowManagerService類的成員函數showSurfaceRobustlyLocked的返回值等于true,那么就說明WindowManagerService服務已經成功地通知SurfaceFlinger服務將WindowState對象w所描述的窗口顯示出來,于是就會分別將WindowState對象w的成員變量mHasDrawn和mLastHidden的值設置為true和false,以便可以表示WindowState對象w所描述的窗口的UI已經繪制完成,并且已經顯示出來。
WindowManagerService類的成員函數showSurfaceRobustlyLocked的實現如下所示:
~~~
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
boolean showSurfaceRobustlyLocked(WindowState win) {
try {
if (win.mSurface != null) {
win.mSurfaceShown = true;
win.mSurface.show();
......
}
return true;
} catch (RuntimeException e) {
......
}
reclaimSomeSurfaceMemoryLocked(win, "show");
return false;
}
......
}
~~~
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類的成員函數showSurfaceRobustlyLocked用來通知SurfaceFlinger服務將參數win所描述的窗口顯示出來,這是通過調用WindowState對象win的成員變量mSurface所指向的一個Surface對象的成員函數show來實現的。注意,在通知SurfaceFlinger服務將WindowState對象win所描述的窗口顯示出來之前,還會將它的成員變量mSurfaceShown的值設置為true。
如果在通知SurfaceFlinger服務將WindowState對象win所描述的窗口顯示出來的過程出現了異常,那么WindowManagerService類的成員函數showSurfaceRobustlyLocked就會調用另外一個成員函數reclaimSomeSurfaceMemoryLocked來回收系統內存資源。
從上面分析可以知道,一個窗口的顯示和隱藏,以及大小、X軸和Y軸位置、Z軸位置、Alpha通道和變換矩陣設置,是通過調用Java層的Surface類的成員函數show、hide、setSize、setPosition、setLayer、setAlpha和setMatrix來實現的,它們都是一些JNI方法,定義在文件frameworks/base/core/java/android/view/Surface.java中,如下所示:
~~~
public class Surface implements Parcelable {
......
private int mSurfaceControl;
......
/**
* set surface parameters.
* needs to be inside open/closeTransaction block
*/
public native void setLayer(int zorder);
public native void setPosition(int x, int y);
public native void setSize(int w, int h);
public native void hide();
public native void show();
......
public native void setAlpha(float alpha);
public native void setMatrix(float dsdx, float dtdx,
float dsdy, float dtdy);
......
}
~~~
這些JNI方法是由C++層中的函數Surface_show、Surface_hide、Surface_setSize、Surface_setPosition、Surface_setLayer、Surface_setAlpha和Surface_setMatrix來實現的,如下所示:
~~~
static void Surface_setLayer(
JNIEnv* env, jobject clazz, jint zorder)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setLayer(zorder);
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setPosition(
JNIEnv* env, jobject clazz, jint x, jint y)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setPosition(x, y);
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setSize(
JNIEnv* env, jobject clazz, jint w, jint h)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setSize(w, h);
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_hide(
JNIEnv* env, jobject clazz)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->hide();
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_show(
JNIEnv* env, jobject clazz)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->show();
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setAlpha(
JNIEnv* env, jobject clazz, jfloat alpha)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setAlpha(alpha);
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setMatrix(
JNIEnv* env, jobject clazz,
jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy)
{
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setMatrix(dsdx, dtdx, dsdy, dtdy);
if (err<0 && err!=NO_INIT)
doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
~~~
這些JNI方法定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。
這些JNI都有一個共同的特點,即先調用函數getSurfaceControl來獲得與參數clazz所描述的一個Java層的Surface對象所對應的一個SurfaceControl對象。有了這個SurfaceControl對象之后,就可以分別調用它的成員函數show、hide、setSize、setPosition、setLayer、setAlpha和setMatrix來通知SurfaceFlinger服務來顯示和隱藏一個窗口,以及設置一個窗口大小、X軸和Y軸位置、Z軸位置、Alpha通道和變換矩陣。
從前面Android應用程序窗口(Activity)的繪圖表面(Surface)的創建過程分析一文可以知道,每一個Activity窗口在Java層都對應有兩個Surface對象,其中一個位于應用程序進程這一側,而另外一個位于WindowManagerService服務這一側。每一個位于應用程序進程這一側的Java層的Surface對象在C++層中都對應有一個Surface對象,而每一個位于WindowManagerService服務這一側的Java層的Surface對象在C++層中都對應有一個SurfaceControl對象,這個C++層的SurfaceControl對象的地址就保存在Java層的Surface對象的成員變量mSurfaceControl中。
從上面的分析可以知道,我們目前正在操作的是正在位于WindowManagerService服務這一側的Java層的Surface對象,因此,通過調用函數getSurfaceControl就可以在C++層中獲得一個對應的SurfaceControl對象,而有了這個SurfaceControl對象之后,就可以用來通知SurfaceFlinger服務更新一個窗口的屬性,這一點可以參考前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃和Android系統Surface機制的SurfaceFlinger服務簡要介紹和學習計劃兩個系列的文章。
至此,WindowManagerService服務計算窗口Z軸位置的過程就分析完成了,這個過程如下所示:
1. WindowManagerService服務將窗口排列在一個窗口堆棧中;
2. WindowManagerService服務根據窗口類型以及窗口在窗口堆棧的位置來計算得窗口的Z軸位置;
3. WindowManagerService服務通過Java層的Surface類的成員函數setLayer來將窗口的Z軸位置設置到SurfaceFlinger服務中去;
4. Java層的Surface類的成員函數setLayer又是通過調用C++層的SurfaceControl類的成員函數setLayer來將窗口的Z軸位置設置到SurfaceFlinger服務中去的;
通過這篇文章以及前面三篇文章(窗口組織、輸入法窗口、壁紙窗口)的學習,我們對WindowManagerService服務對窗口的管理就有一個比較深刻的認識了,在接下來的文章中,我們還將繼續分析ActivityWindowManager服務和WindowManagerService服務是如何協作來完成Activity窗口的顯示過程的,敬請關注!
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析