本文基于Android 11.0源碼分析
前言
??在Window的添加過程中,主要添加窗口時的WindowManagerService#addWindow()方法進行了主要總結,通過WindowManagerService#addWindow()方法,system_server中創建了一個對應的窗口管理對象,并且放置到了適合的容器中。這篇文章中將對之后窗口的relayout過程進行總結。
1.requestLayout過程
??回到添加窗口流程中的ViewRootImpl#setView()方法中,在調用mWindowSession#addToDisplayAsUser()方法之前,先是調用了requestLayout()方法,并最終通過Chreographer對象設置了一個VSync監聽:
// frameworks/base/core/java/android/view/ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 設置一個同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 設置VSync監聽
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
當收到VSync信號后,mTraversalRunnable線程的run方法:
//frameworks/base/core/java/android/view/ViewRootImpl.java
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
執行doTraversal()方法進行遍歷操作:
//frameworks/base/core/java/android/view/ViewRootImpl.java
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
在performTraversals方法中執行relayoutWindow()方法,通過IWindowSession接口實例向WMS發起布局請求:
//frameworks/base/core/java/android/view/ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize, mBlastSurfaceControl);
......
return relayoutResult;
}
這里Session.java的relayout()方法,之后將進入WMS#relayoutWindow()方法中,在這個方法中進行布局工作。下面詳細來看這個方法。
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
/**
* @param session IWindowSession對象,用于ViewRootImpl向WMS發起交互
* @param client IWindow對象,代表客戶端的Window,用于WMS向ViewRootImpl發起交互
* @param seq 請求序列
* @param attrs 窗口各種參數和屬性
* @param requestedWidth 客戶端請求窗口的寬
* @param requestedHeight 客戶端請求窗口的高
* @param viewVisibility View的可見性
* @param flags 標記
* @param frameNumber
* @param outFrame 返回給ViewRootImpl的窗口框架
* @param outContentInsets
* @param outVisibleInsets
* @param outStableInsets
* @param outBackdropFrame
* @param outCutout
* @param mergedConfiguration
* @param outSurfaceControl 返回給ViewRootImpl的Surface管理對象
* @param outInsetsState
* @param outActiveControls
* @param outSurfaceSize
* @param outBLASTSurfaceControl
* @return
*/
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
......
boolean configChanged;
synchronized (mGlobalLock) {
// 獲取WindowState、DisplayContent、DisplayPolicy、WindowStateAnimator對象
final WindowState win = windowForClientLocked(session, client, false);
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator;
// 給WindowState設置來自應用請求的窗口大小
if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight);
}
// 設置Frame number
win.setFrameNumber(frameNumber);
final DisplayContent dc = win.getDisplayContent();
// 如果此時沒有執行Configuration的更新,試圖結束銜接動畫
if (!dc.mWaitingForConfig) {
win.finishSeamlessRotation(false /* timeout */);
}
// 用來標記屬性是否發生變化
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
int privflagChanges = 0;
if (attrs != null) {
// 調整特殊類型的Window#attrs屬性
displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
// 針對壁紙窗口調整Window#attrs屬性
win.mToken.adjustWindowParams(win, attrs);
// 調整mSystemUiVisibility屬性,控制status bar的顯示
if (seq == win.mSeq) {
int systemUiVisibility = attrs.systemUiVisibility
| attrs.subtreeSystemUiVisibility;
......
}
win.mSystemUiVisibility = systemUiVisibility;
}
// PRIVATE_FLAG_PRESERVE_GEOMETRY將忽略新x、y、width、height值,使用舊值
if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
!= 0) {
attrs.x = win.mAttrs.x;
attrs.y = win.mAttrs.y;
attrs.width = win.mAttrs.width;
attrs.height = win.mAttrs.height;
}
// 確定flag是否發生變化
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
// 根據flag是否發生變化做出對應響應,略....
.......
}
// 根據應用請求設置寬高,獲取窗口縮放比例
win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
// 窗口此時的可見狀態
final int oldVisibility = win.mViewVisibility;
// 窗口是否要由不可見狀態轉變為可見狀態
final boolean becameVisible =
(oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
&& viewVisibility == View.VISIBLE;
// 是否需要移除IME窗口
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
// 確定是否需要更新focus狀態,第一次執行relayout時,mRelayoutCalled為false
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
// 如果窗口可見性發生變化,且該窗口允許顯示在壁紙之上,則對壁紙窗口進行處理
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
win.mRelayoutCalled = true; // 設置WindowState#mRelayoutCalled為true
win.mInRelayout = true; // 設置WindowState#mInRelayout為true,表示在relayout過程中,relayout完畢后,重置為false
// 更新窗口的可見性
win.setViewVisibility(viewVisibility);
// 通知DisplayContent,需要進行重新布局
win.setDisplayLayoutNeeded();
// 表示是否在等待設置Inset
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
// 如果窗口可見,進行重新布局
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
// 執行一遍刷新操作
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
// 是否需要進行relayout操作
if (shouldRelayout) {
// 布局
result = win.relayoutVisibleWindow(result, attrChanges);
try {
// 創建Surface
result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
result, win, winAnimator);
} catch (Exception e) {
return 0;
}
// 如果是第一次relayout操作,需要focusMayChange
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
// 輸入法窗口的特殊處理
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& displayContent.mInputMethodWindow == null) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = true;
}
} else {
......
}
// 更新FocusWindow
if (focusMayChange) {
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
// 是否首次更新
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
// 更新屏幕顯示方向
configChanged = displayContent.updateOrientation();
// 對壁紙窗口的特殊處理,更新偏移量
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}
......
// 更新mergedConfiguration對象
if (shouldRelayout) {
win.getMergedConfiguration(mergedConfiguration);
} else {
win.getLastReportedMergedConfiguration(mergedConfiguration);
}
// 設置各種Inset和DisplayCutout
win.getCompatFrame(outFrame);
win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
outStableInsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
outInsetsState.set(win.getInsetsState(), win.isClientLocal());
// 更新relayout標記,表示可接受touch事件
result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
win.mInRelayout = false; // 重置為false,表示relayout過程完成
// configuration發生變化時,更新全局Configuration
if (configChanged) {
displayContent.sendNewConfiguration();
}
// 設置outSurfaceSize
if (winAnimator.mSurfaceController != null) {
outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
winAnimator.mSurfaceController.getHeight());
}
getInsetsSourceControls(win, outActiveControls);
}
return result;
}
在進入這個方法后,首先通過IWindow對象獲取了對應的WindowState對象,之后將以WindowState為主體進行各種操作。這個方法很長,但主要有以下幾個步驟:
WindowState#setRequestedSize()設置WindowState寬高;
DisplayPolicy#adjustWindowParamsLw()調整窗口屬性;
WindowState#setWindowScale()設置縮放比例;
WindowState#setViewVisibility()更新可見性屬性;
WindowState#relayoutVisibleWindow()處理FLAG_TURN_SCREEN_ON標記;
WMS#createSurfaceControl()創建Surface;
WMS#updateFocusedWindowLocked()更新焦點窗口;
設置MergedConfiguration對象返回客戶端;
設置windowFrame和Inset返回客戶端。
2.relayoutWindow逐步分析
??上一小節分析requestLayout()過程中,最終會調用到WMS的relayoutWindow()方法,該方法中有很多的處理過程,下面對這些步驟進行分析總結。
2.1 WindowState#setRequestedSize()設置WindowState寬高
??通過WindowState#setRequestedSize()方法,設置WindowState對象mRequestedWidth和mRequestedHeight屬性,這兩屬性表示來自應用請求設置的寬高:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void setRequestedSize(int requestedWidth, int requestedHeight) {
if ((mRequestedWidth != requestedWidth || mRequestedHeight != requestedHeight)) {
mLayoutNeeded = true;
mRequestedWidth = requestedWidth;
mRequestedHeight = requestedHeight;
}
}
2.2 DisplayPolicy#adjustWindowParamsLw()調整窗口屬性
??接下來,會根據系統當前條件,對客戶端傳入的布局參數進行調整,主要通過DisplayPolicy#adjustWindowParamsLw(win, attrs, pid, uid)調整,對于壁紙這類特殊窗口,會通過WallpaperWindowToken#adjustWindowParams()方法進行調整。如果調整后的布局參數發生了變化,則進行相關狀態的處理。這部分代碼略過。
2.3 WindowState#setWindowScale()設置縮放比例
??通過WindowState#setWindowScale()方法,設置WindowState對象mHScale和mVScale屬性,這兩屬性表示窗口在橫豎方向的縮放比例:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void setWindowScale(int requestedWidth, int requestedHeight) {
final boolean scaledWindow = (mAttrs.flags & FLAG_SCALED) != 0;
if (scaledWindow) {
mHScale = (mAttrs.width != requestedWidth) ?
(mAttrs.width / (fl性oat)requestedWidth) : 1.0f;
mVScale = (mAttrs.height != requestedHeight) ?
(mAttrs.height / (float)requestedHeight) : 1.0f;
} else {
mHScale = mVScale = 1;
}
}
可以看到,只有對在布局參數中設置了FLAG_SCALED標記的窗口,才會計算縮放比例,否則都是1。
2.4 WindowState#setViewVisibility()更新可見性
??通過WindowState#setViewVisibility()方法,設置WindowState對象mViewVisibility屬性,該屬性表示該Window的可見性:
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void setViewVisibility(int viewVisibility) {
mViewVisibility = viewVisibility;
......
}
mViewVisibility有三個值,均來自于APP端的View:
View.VISIBLE:表示該窗口可見;
View.INVISIBLE:表示該窗口不可見,但仍然占據空間,其Surface依然存在;
View.GONE:表示該窗口不可見,并且不再占據空間,其Surface也被銷毀。
2.5 WindowState#relayoutVisibleWindow()處理FLAG_TURN_SCREEN_ON
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
int relayoutVisibleWindow(int result, int attrChanges) {
// 是否可見
final boolean wasVisible = isVisibleLw();
// 如果不可見,表示第一次進行relayout
result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
// 是否執行退出動畫
if (mAnimatingExit) {
....
}
// 是否在銷毀Surface列表中,等待被Destory
if (mDestroying) {
mDestroying = false;
mWmService.mDestroySurface.remove(this);
}
if (!wasVisible) {
// 表示即將執行進入動畫
mWinAnimator.mEnterAnimationPending = true;
}
// 最近一次可見時的屏幕方向
mLastVisibleLayoutRotation = getDisplayContent().getRotation();
// 表示開始執行進入動畫
mWinAnimator.mEnteringAnimation = true;
try {
prepareWindowToDisplayDuringRelayout(wasVisible);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
// 當布局參數中像素格式發生變化后的處理
if ((attrChanges & FORMAT_CHANGED) != 0) {
.....
}
// 當拖拽發生resize時
if (isDragResizeChanged()) {
......
}
......
return result;
}
在這個方法中,調用prepareWindowToDisplayDuringRelayout()方法,針對亮屏flag進行處理:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
// 是否帶有點亮屏幕的標記
final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
|| (mActivityRecord != null && mActivityRecord.canTurnScreenOn());
if (hasTurnScreenOnFlag) {
......
if (allowTheaterMode && canTurnScreenOn
&& (mWmService.mAtmInternal.isDreaming()
|| !mPowerManagerWrapper.isInteractive())) {
// 觸發亮屏
mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
}
if (mActivityRecord != null) {
mActivityRecord.setCurrentLaunchCanTurnScreenOn(false);
}
}
if (wasVisible) {
return;
}
......
}
以上方法中,如果布局參數或對應的ActivityRecord中攜帶有亮屏相關flag,則進行亮屏。窗口中通過FLAG_TURN_SCREEN_ON進行亮屏,就是在這里觸發的。
2.6 WMS#createSurfaceControl()創建Surface
??在客戶端ViewRootImpl中,有一個Surface對象,它是這個ViewRootImpl中View顯示內容的最終呈現者。但它在創建時,僅僅是一個空殼,沒有任何內容。在relayout()時,ViewRootImpl將SurfaceControl對象傳遞給了WMS,WMS中會創建一個SurfaceControl對象,并將它復制給傳入的SurfaceControl,最終ViewRootImpl從SurfaceControl中獲取內容:
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private int createSurfaceControl(SurfaceControl outSurfaceControl,
SurfaceControl outBLASTSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
// 此時該WindowState還沒有對應Surface
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
WindowSurfaceController surfaceController;
try {
// 創建一個WindowSurfaceController對象
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
} finally {
}
if (surfaceController != null) {
// 將WindowSurfaceController中的mSurfaceControl復制給outSurfaceControl
surfaceController.getSurfaceControl(outSurfaceControl);
surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
}
return result;
}
以上方法中:
首先給result變量設置RELAYOUT_RES_SURFACE_CHANGED標記,表示relayout過程的第二個階段;
然后通過WindowStateAnimator#createSurfaceLocked()創建了WindowSurfaceController對象;
最后通過WindowSurfaceController將內部SurfaceControl拷貝給了outSurfaceControl,outSurfaceControl將返回給客戶端的ViewRootImpl。
??WindowStateAnimator對象的作用是執行和管理WindowState動畫和Surface操作,而WindowSurfaceController就是為WindowStateAnimator對象管理SurfaceControl對象。
??創建WindowSurfaceController對象如下:
//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
final WindowState w = mWin;
// 如果已經存在,直接返回
if (mSurfaceController != null) {
return mSurfaceController;
}
// 設置WindowState#mHasSurface屬性為false,表示沒有Surface
w.setHasSurface(false);
// 重置繪制狀態
resetDrawState();
//
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
// 計算Surface區域大小,relayout時,由于沒有填充Frame,因此獲取區域為0
calculateSurfaceBounds(w, attrs, mTmpSize);
try {
......
// 創建WindowSurfaceController對象
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
height, format, flags, this, windowType, ownerUid);
mSurfaceFormat = format;
// 設置WindowState#mHasSurface屬性為false,表示沒有Surface
w.setHasSurface(true);
} catch (OutOfResourcesException e) {
}
mLastHidden = true;
return mSurfaceController;
}
在創建WindowSurfaceController對象時,將會創建對應的SurfaceControl對象:
//frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java
WindowSurfaceController(String name, int w, int h, int format,
int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
mAnimator = animator; // WindowStateAnimator對象
mSurfaceW = w; // Surface的寬,創建時為1, 在setBufferSizeInTransaction()中會設置具體值
mSurfaceH = h; // Surface的高,創建時為1
title = name; // 窗口名
mService = animator.mService; // WMS對象
final WindowState win = animator.mWin; // WindowState對象
mWindowType = windowType; // Window類型
mWindowSession = win.mSession; // IWindowSession對象
mTmpTransaction = mService.mTransactionFactory.get();
// 創建SurfaceControl
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl())
.setName(name)
.setBufferSize(w, h)
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, ownerUid)
.setCallsite("WindowSurfaceController");
mSurfaceControl = b.build();
}
創建完成WindowSurfaceController對象后, 通過WindowState#setHasSurface()將WindowState#mHasSurface屬性設置為true,表示該WindowState已經擁有了Surface。
2.7 WMS#updateFocusedWindowLocked()更新焦點窗口
??接下來,會通過WMS#updateFocusedWindowLocked()方法,進行焦點窗口的更新。
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return changed;
}
這里就是直接調用RootWindowContainer#updateFocusedWindowLocked():
//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
mTopFocusedAppByProcess.clear();
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
final WindowState newFocus = dc.mCurrentFocus;
if (newFocus != null) {
final int pidOfNewFocus = newFocus.mSession.mPid;
if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = dc.getDisplayId();
}
} else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
// The top-most display that has a focused app should still be the top focused
// display even when the app window is not ready yet (process not attached or
// window not added yet).
topFocusedDisplayId = dc.getDisplayId();
}
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = DEFAULT_DISPLAY;
}
if (mTopFocusedDisplayId != topFocusedDisplayId) {
mTopFocusedDisplayId = topFocusedDisplayId;
mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d",
topFocusedDisplayId);
}
return changed;
}
updateFocusedWindowLocked()獲取輪詢RootWindowContainer容器中的對象,然后通過對象的DisplayContent去更新焦點窗口。
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
int topFocusedDisplayId) {
// 查找當前焦點窗口
WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
// 判斷查找的結果和當前的焦點窗口是否一樣
if (mCurrentFocus == newFocus) {
return false;
}
boolean imWindowChanged = false;
// 輸入法窗口
final WindowState imWindow = mInputMethodWindow;
if (imWindow != null) {
final WindowState prevTarget = mInputMethodTarget;
final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
imWindowChanged = prevTarget != newTarget;
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
assignWindowLayers(false /* setLayoutNeeded */);
}
}
if (imWindowChanged) {
mWmService.mWindowsChanged = true;
setLayoutNeeded();
newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
}
// 焦點窗口變化處理
if (mCurrentFocus != newFocus) {
mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
}
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
if (newFocus != null) {
mWinAddedSinceNullFocus.clear();
mWinRemovedSinceNullFocus.clear();
if (newFocus.canReceiveKeys()) {
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
newFocus.mToken.paused = false;
}
}
// 通知焦點窗口變化了
onWindowFocusChanged(oldFocus, newFocus);
int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayout(true /*initial*/, updateInputWindows);
focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
assignWindowLayers(false /* setLayoutNeeded */);
}
}
if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
// The change in focus caused us to need to do a layout. Okay.
setLayoutNeeded();
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayout(true /*initial*/, updateInputWindows);
} else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
mWmService.mRoot.performSurfacePlacement();
}
}
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for doing this part.
getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
}
adjustForImeIfNeeded();
// We may need to schedule some toast windows to be removed. The toasts for an app that
// does not have input focus are removed within a timeout to prevent apps to redress
// other apps' UI.
scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
}
return true;
}
最后通過onWindowFocusChanged()去通知其他模塊焦點窗口變化了。
2.8 設置MergedConfiguration對象給客戶端
??接下來更新MergedConfiguration對象,MergedConfiguration是全局Configuration對象和override Configurationu對象的包裝類,實現了Parcelable對象,因此可以用來進程間傳遞,它的主要作用就是WMS向ViewRootImpl同步Configuration。在relayoutWindow()時,會將當前global configuration 和override configuration給到MergedConfiguration,并創建出一個新的Configuration對象:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void getMergedConfiguration(MergedConfiguration outConfiguration) {
// 獲取全局Configuration對象
final Configuration globaluConfig = getProcessGlobalConfiguration();
// 獲取覆蓋Configuration對象
final Configuration overrideConfig = getMergedOverrideConfiguration();
// 設置給MergedConfiguration
outConfiguration.setConfiguration(globalConfig, overrideConfig);
}
2.9 設置windowFrame和Inset給客戶端
?? WMS中的WindowFrame類,其中攜帶了一些影響窗口如何進行布局的Rect對象,比較重要的一些Rect對象有:
mDisplayFrame:來自于DisplayFrame#mUnrestricted屬性,表示此窗口所在的ActivityStack的整個屏幕區域,一般等于邏輯屏大小;
mParentFrame:來自于mDisplayFrame,輸入法、壁紙等特殊窗口除外,多數情況下,等于mDisplayFrame;
mContainingFrame:對于full-screen類型窗口來說,等于mParentFrame。
??這些Rect在DisplayPolicy#layoutWindowLw()方法中對它們進行了設置,具體流程在Surface的placement流程中說明。
??這里會將WindowFrame#mCompatFrame以及其他幾個Rect對象設置給ViewRootImpl中傳入的變量,從而輸出給ViewRootImpl:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void getCompatFrame(Rect outFrame) {
outFrame.set(mWindowFrames.mCompatFrame);
}
void getInsetsForRelayout(Rect outContentInsets, Rect outVisibleInsets,
Rect outStableInsets) {
outContentInsets.set(mWindowFrames.mContentInsets);
outVisibleInsets.set(mWindowFrames.mVisibleInsets);
outStableInsets.set(mWindowFrames.mStableInsets);
mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
}
3.總結
通過relayout操作,更新對應WindowState的大小、可見性等相關屬性;
在第一次relayout時,就會創建一個Surface,并對Surface進行大小等屬性的設置,返回給客戶端,作為View顯示內容的載體;
創建Surface時,通過WindowStateAnimator中的WindowSurfaceController進行創建。
??Surface創建好之后,返回給可客戶端,客戶端會將具體內容的畫在Surface上,并在完成后通知WMS。WMS中將通過WindowSurfacePlacer#performSurfacePlacement()方法,最終將Surface送給SurfaceFlinger,完成顯示。
??以上整個過程時序圖如下: