上一篇文章分析了焦点窗口更新框架,本文从实战的角度,分析 app 冷启动的焦点窗口更新。
本文是前面一系列文章的集大成者,因此阅读本文之前,需要读者有一定的 WMS 功底,或者详细阅读前面写的文章,否则你可能不知道我在说什么。
第一阶段启动
第一阶段启动中,会添加启动窗口。ViewRootImpl 会向 WMS 发起 add window,这里涉及焦点窗口更新,如下
java
// WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
float[] outSizeCompatScale) {
// ...
synchronized (mGlobalLock) {
// ...
boolean focusChanged = false;
// 窗口能接收按键事件,需要更新焦点窗口
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
// ...
// 更新 layer
if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) {
// Assign child layers from the parent Task if the Activity is embedded.
win.getTask().assignChildLayers();
} else {
win.getParent().assignChildLayers();
}
// 如果焦点窗口改变,下发焦点窗口
if (focusChanged) {
displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
// 调度更新输入窗口
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
// ...
}
// ...
}
add window 中,如果需要更新焦点窗口,需要窗口能接收按键事件,如下
java
// WindowState.java
boolean canReceiveKeys() {
return canReceiveKeys(false /* fromUserTouch */);
}
public boolean canReceiveKeys(boolean fromUserTouch) {
// ...
final boolean canReceiveKeys = isVisibleRequestedOrAdding()
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
// 窗口必须能获得焦点
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
// can it receive touches
&& (mActivityRecord == null || mActivityRecord.getTask() == null
|| !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());
if (!canReceiveKeys) {
return false;
}
return fromUserTouch || getDisplayContent().isOnTop()
|| getDisplayContent().isTrusted();
}
但是,由于添加启动窗口的 atrrs 中包含 NOT_FOCUSABLE,因此启动窗口不能获焦,log 如下
txt
WindowState: Window Window{44b1e82 u0 Splash Screen com.awesome.helloworld} client=android.os.BinderProxy@b0e05cd token=ActivityRecord{9b0f41e u0 com.awesome.helloworld/.MainActivity t25} (Token{25b321b ActivityRecord{9b0f41e u0 com.awesome.helloworld/.MainActivity t25}}) params={(0,0)(fillxfill) ty=APPLICATION_STARTING fmt=TRANSLUCENT wanim=0x10302fe
WindowState: fl=NOT_FOCUSABLE NOT_TOUCHABLE LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR ALT_FOCUSABLE_IM HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
WindowState: pfl=SHOW_FOR_ALL_USERS USE_BLAST FIT_INSETS_CONTROLLED
WindowState: bhv=DEFAULT
WindowState: fitSides=}
因此,启动窗口的 add window 不能更新焦点窗口。
ViewRootImpl 为启动窗口 add window 后,会向 WMS 发起 relayout window,这也会触发焦点窗口更新,如下
java
// WindowManagerService.java
private int relayoutWindowInner(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
int lastSyncSeqId, ClientWindowFrames outFrames,
MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
Bundle outBundle, WindowRelayoutResult outRelayoutResult) {
// ...
synchronized (mGlobalLock) {
// ...
// 启动窗口第一次relayout,win.mRelayoutCalled 为 false
// 所以,focusMayChange 为 true
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
// ...
if (shouldRelayout) {
// ...
// 第一次 relayout,result 会有 RELAYOUT_RES_FIRST_TIME
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
// ...
} else {
// ...
}
if (focusMayChange) {
// 更新焦点窗口,
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
// ...
}
// ...
}
虽然说,启动窗口第一次 relayout,会触发焦点窗口更新。但是,由于启动窗口不能获焦,它不能成为焦点窗口。因此,此时焦点窗口更新,没有导致焦点窗口改变。
第二阶段启动
第二阶段启动,会触发 activity 可见性更新。要启动的 ActivityRecord 的 visible requested 更新为 true,而 Launcher ActivityRecord 的更新为 false。
由于有 activity 可见性改变,第二阶段启动,会发起一次窗口刷新。在窗口刷新中,会强制 update input windows,把最新的窗口信息,下发给 SurfaceFlinger,如下
java
// RootWindowContainer.java
void performSurfacePlacementNoTrace() {
// ...
forAllDisplays(dc -> {
// 参数为 true,表示强制 update input windows
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
// ...
});
// ...
}
此次强制 update input windows,由于 ActivityRecord 的 visible requested 改变,会影响窗口的可聚焦属性,来看下窗口信息填充过程,如下
java
// InputMonitor.java
void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle,
final WindowState w) {
// ...
// 如果 ActivityRecord#mVisibleRequested 为 false,窗口是无法获得焦点
final boolean focusable = w.canReceiveKeys()
&& (mDisplayContent.hasOwnFocus() || mDisplayContent.isOnTop());
inputWindowHandle.setFocusable(focusable);
// ...
}
java
// WindowState.java
public boolean canReceiveKeys(boolean fromUserTouch) {
// ...
// ActivityRecord 的 visible requested 为 false,不能接收按键事件
final boolean canReceiveKeys = isVisibleRequestedOrAdding()
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
// can it receive touches
&& (mActivityRecord == null || mActivityRecord.getTask() == null
|| !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());
if (!canReceiveKeys) {
return false;
}
return fromUserTouch || getDisplayContent().isOnTop()
|| getDisplayContent().isTrusted();
}
boolean isVisibleRequestedOrAdding() {
final ActivityRecord atoken = mActivityRecord;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& isVisibleByPolicy() && !isParentWindowHidden()
// ActivityRecord 的 visible requesed 必须为 true
&& (atoken == null || atoken.isVisibleRequested())
&& !mAnimatingExit && !mDestroying;
}
由于 Launcher ActivityRecord 的 visible requested 更新为 false,因此 Launcher 窗口的不可聚焦的属性,被下发到 SurfaceFlinger,最终下发到了输入系统的 InputDispatcher。InputDispatcher 会利用所有的窗口信息,以及焦点窗口请求,重新解析焦点窗口。判断是否能成为焦点窗口的函数,如下
cpp
// FocusResolver.cpp
// token : 是焦点请求的焦点窗口的token
// windows: 所有窗口信息
// outFocusableWindow: 保存最新解析出的焦点窗口
FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
sp<WindowInfoHandle>& outFocusableWindow) {
bool allWindowsAreFocusable = true;
bool windowFound = false;
sp<WindowInfoHandle> visibleWindowHandle = nullptr;
for (const sp<WindowInfoHandle>& window : windows) {
// 过滤非焦点窗口请求的焦点窗口
if (window->getToken() != token) {
continue;
}
// 走到这里,表示焦点窗口请求中的焦点窗口,匹配到窗口信息
windowFound = true;
// 焦点窗口请求中的焦点窗口,必须可见
if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
// Check if at least a single window is visible.
visibleWindowHandle = window;
}
// 焦点窗口请求中的焦点窗口,必须能获得焦点
if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
// Check if all windows with the window token are focusable.
allWindowsAreFocusable = false;
break;
}
}
if (!windowFound) {
return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowHandle) {
return Focusability::NOT_VISIBLE;
}
// Only set the outFoundWindow if the window can be focused
outFocusableWindow = visibleWindowHandle;
return Focusability::OK;
}
由于到现在为止,还没有下发新的焦点窗口请求。因此,此时的焦点窗口请求中的焦点窗口,仍然是 Launcher 窗口。
然而,此时下发到 InputDispatcher 的窗口信息中,Launcher 窗口不可获焦。因此,此次解析出的焦点窗口为 null。上层的 WMS,此时会收到焦点窗口改变的回调,并打印一行 log,如下
java
// WindowManagerService.java
void reportFocusChanged(IBinder oldToken, IBinder newToken) {
InputTarget lastTarget;
InputTarget newTarget;
synchronized (mGlobalLock) {
lastTarget = getInputTargetFromToken(oldToken);
newTarget = getInputTargetFromToken(newToken);
if (newTarget == null && lastTarget == null) {
Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
return;
}
mFocusedInputTarget = newTarget;
mAccessibilityController.onFocusChanged(lastTarget, newTarget);
// 底层通知的焦点窗口改变的 log
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
}
// ...
}
第三阶段启动
第三阶段启动,会执行 attach app,从而真正的通知 app 端 launch activity,如下
java
// ActivityTaskSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
// ...
try {
// ...
try {
// ...
final LaunchActivityItem launchActivityItem = LaunchActivityItem.obtain(r.token,
r.intent, System.identityHashCode(r), r.info,
procConfig, overrideConfig, deviceId,
r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken,
r.initialCallerInfoAccessToken, activityWindowInfo);
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(r.token, isTransitionForward,
r.shouldSendCompatFakeFocus());
}
// ...
// 1. 通知 app 端 launch activity
mService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
proc.getThread(), launchActivityItem, lifecycleItem,
// Immediately dispatch the transaction, so that if it fails, the server can
// restart the process and retry now.
true /* shouldDispatchImmediately */);
// ...
} catch (RemoteException e) {
// ...
}
} finally {
// ...
}
r.launchFailed = false;
if (andResume && readyToResume()) {
// 2. 更新 ActivityRecord 状态为 RESUMED
r.setState(RESUMED, "realStartActivityLocked");
r.completeResumeLocked();
} else if (r.isVisibleRequested()) {
// ...
} else {
// ...
}
// ...
return true;
}
服务端通知 app 端 launch activity 后,没有等待 app 端反馈 activity resumed,直接把 ActivityRecord 状态设置为了 RESUMED,如下
java
// ActivityRecord.java
void setState(State state, String reason) {
ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s",
this, getState(), state, reason);
// ...
mState = state;
if (getTaskFragment() != null) {
// 通知 TaskFragment,其下的 ActivityRecord 生命周期状态改变
getTaskFragment().onActivityStateChanged(this, state, reason);
}
// ...
}
java
// TaskFragment.java
void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
String reason) {
// ...
if (state == RESUMED) {
if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason);
}
// TaskFragment 保存 resumed activity
setResumedActivity(record, reason + " - onActivityStateChanged");
// ...
}
// ...
}
void setResumedActivity(ActivityRecord r, String reason) {
// ...
final ActivityRecord prevR = mResumedActivity;
// TaskFragment 使用 mResumedActivity 保存其下的 resumed activity
mResumedActivity = r;
// 通知 ActivityTaskSupervisor 更新 top resumed activity
final ActivityRecord topResumed = mTaskSupervisor.updateTopResumedActivityIfNeeded(reason);
// ...
}
java
// ActivityTaskSupervisor.java
ActivityRecord updateTopResumedActivityIfNeeded(String reason) {
final ActivityRecord prevTopActivity = mTopResumedActivity;
// 现在 top root task 是启动的 app root task
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
// ...
// 保存 top resumed activity
mTopResumedActivity = topRootTask.getTopResumedActivity();
// ...
// ATMS 更新 resumed activity 和 focusd app
if (mTopResumedActivity != null) {
mService.setLastResumedActivityUncheckLocked(mTopResumedActivity, reason);
}
// ...
return mTopResumedActivity;
}
java
// ActivityTaskManagerService.java
void setLastResumedActivityUncheckLocked(ActivityRecord r, String reason) {
// ...
// ATMS 保存 resumed activity
mLastResumedActivity = r;
// Don't take focus when transient launching. We don't want the app to know anything
// until we've committed to the gesture. The focus will be transferred at the end of
// the transition (if the transient launch is committed) or early if explicitly requested
// via `setFocused*`.
boolean focusedAppChanged = false;
// 当前不是 transient launch
if (!getTransitionController().isTransientCollect(r)) {
// 1.设置焦点 app
focusedAppChanged = r.mDisplayContent.setFocusedApp(r);
// 2. 焦点app改变了,更新焦点窗口
if (focusedAppChanged) {
mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
}
}
// ...
// ATMS 更新 resumed activity 的 event log
EventLogTags.writeWmSetResumedActivity(r.mUserId, r.shortComponentName, reason);
}
由于启动的 ActivityRecord 的状态切换到 RESUMED,导致焦点 app 改变,并下发到底层,最终 InputDispatcher 用一个数据结构保存了这个焦点 app。
另外,由于焦点 app 改变,此时还需要更新一次焦点窗口。在寻找焦点窗口时,由于此时 Launcher ActivityRecord visible requested 为 false,导致找到的焦点窗口为 null。因此,此次焦点窗口更新,下发的焦点窗口请求中的焦点窗口为 null。InputDispatcher 利用 FocusResolver 最终解析出的焦点窗口也为 null。
由于前面刚解析出的焦点窗口为 null,而此次又解析出 null,因此上层不会收到焦点窗口改变的回调。
app 端 launch activity 时, Activity 会创建 DecorView,并通过 WindowMangerGlobal 添加,如下
java
// ActivityThread.java
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
// ...
// 执行 resume activity
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
// ...
final Activity a = r.activity;
// ...
// a.mStartedActivity 表示是否从当前 Activity 启动另外一个 activity
// willBeVisible 此时为 true
boolean willBeVisible = !a.mStartedActivity;
// ...
if (r.window == null && !a.mFinished && willBeVisible) {
// 此时 ActivityClientRecord#window 才保存了 Activity 创建的 PhoneWindow
r.window = r.activity.getWindow();
// DecorView 的可见性,先设置为 INVISIBLE
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 通过 Activity 创建的 WindowManager 来添加 DecorView
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
// ...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
}
}
// ...
} else if (!willBeVisible) {
}
// ...
}
但是,需要注意,此时的 DecorView 的可见性为 INVISIBLE。ViewRootImpl 会利用这个可见性发起一次 add window,此时的 add winddow 是添加 Activity 真窗。
前面指出过,add window 可能会导致焦点窗口改变。然而,此次为真窗发起的 add window,由于可见性为 INVISIBLE,不会更新焦点窗口,如下
java
// WindowManagerService.java
// 注意,viewVisibility 为 INVISIBLE
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
float[] outSizeCompatScale) {
// ...
synchronized (mGlobalLock) {
// ...
// 创建 WindowState
// 注意,此时 WindowState 保存了 View 可见性 viewVisibility,它的值为 INVISIBLE
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// ...
boolean focusChanged = false;
// 由于真窗的 View 可见性为 INVISIBLE,不能接收按键事件
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
// ...
}
// ...
}
java
// WindowState.java
public boolean canReceiveKeys(boolean fromUserTouch) {
// ...
final boolean canReceiveKeys = isVisibleRequestedOrAdding()
// View 可见性此时为 INVISIBLE,因此窗口不能获焦
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
// can it receive touches
&& (mActivityRecord == null || mActivityRecord.getTask() == null
|| !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());
if (!canReceiveKeys) {
return false;
}
// Do not allow untrusted virtual display to receive keys unless user intentionally
// touches the display.
return fromUserTouch || getDisplayContent().isOnTop()
|| getDisplayContent().isTrusted();
}
Activity 通过 WindowManagerGlobal 添加了 DecorView (可见性为 INVISIBLE),ViewRootImpl 除了会发起一次 add window,还会向 Choreographer 请求一次 traversal。
Activity 之后把 DecorView 的可见性更新为 VISIBLE,如下
java
// ActivityThread.java
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
// ...
if (r.window == null && !a.mFinished && willBeVisible) {
// 此时 ActivityClientRecord#window 才保存了 Activity 创建的 PhoneWindow
r.window = r.activity.getWindow();
// DecorView 的可见性,先设置为 INVISIBLE
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 1. 通过 Activity 创建的 WindowManager 来添加 DecorView
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// ...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
}
}
// ...
} else if (!willBeVisible) {
}
// ...
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
// ...
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
// 2. 把 DecorView 设置为 VISIBLE
r.activity.makeVisible();
}
// ...
}
// ...
}
那么,当 ViewRootImpl 执行 traversal 时,View 的可见性为 VISIBLE。然后利用这个可见性,为真窗发起了一次 relayout window,如下
java
// WindowManagerService.java
// 注意,viewVisibility 为 VISIBLE
private int relayoutWindowInner(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
int lastSyncSeqId, ClientWindowFrames outFrames,
MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
Bundle outBundle, WindowRelayoutResult outRelayoutResult) {
// ...
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, client, false);
// ...
// add window 是,win.mViewVisibility 保存的 View 可见性为 INVISIBLE
// 此时 viewVisibility 为 VISIBLE
// 因此,focusMayChange 为 true
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
// ...
// WindowState#mViewVisibility 保存 View Visibility
win.setViewVisibility(viewVisibility);
// ...
// true
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
if (shouldRelayout && outSurfaceControl != null) {
try {
// 创建真窗 surface
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
// ...
}
}
// ...
if (shouldRelayout) {
// ...
// 真窗第一次 relayout,所以 result 包含 RELAYOUT_RES_FIRST_TIME
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
// ...
} else {
// ...
}
if (focusMayChange) {
// 触发焦点窗口更新
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
// ...
}
// ...
}
真窗 relayout window 触发了焦点窗口更新,真窗可以作为焦点窗口,因此焦点窗口从 null 切换到启动的 app 的窗口,并向底层 update input windows(包括真窗信息)以及发送焦点窗口请求(焦点窗口为真窗)。
此时,底层会把焦点窗口,更新为 app 真窗吗?如果你认为是,那么恭喜你,你还年轻。 真窗 relayout window 创建 surface 时,这个 surface 处于 hidden 状态,如下
java
// WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked() {
// ...
// 创建窗口 surface,默认为 hidden 状态
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
//...
try {
// ...
// 创建窗口 surface
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
flags, this, attrs.type);
// ...
}
// ...
}
当 SurfaceFlinger 收到真窗信息时,会重新填充一份窗口信息,发送到 InputDispatcher。来看下填充窗口信息中,关于可见性的代码部分,如下
出于写作环境限制,这里被迫使用 Android U 的 SurfaceFlinger 代码。
cpp
// SurfaceFlinger.cpp
WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
// ...
WindowInfo info = mDrawingState.inputInfo;
// ...
// 填充可见性
info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput());
// ...
return info;
}
isVisibleForInput() 决定了 Layer 的可见性,如下
cpp
// Layer.h
/*
* Whether or not the layer should be considered visible for input calculations.
*/
virtual bool isVisibleForInput() const {
// 真窗有 input channel,因此这里使用 canReceiveInput() 来判断 Layer 可见性
return hasInputInfo() ? canReceiveInput() : isVisible();
}
cpp
// Layer.cpp
bool Layer::canReceiveInput() const {
return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
}
bool Layer::isHiddenByPolicy() const {
const State& s(mDrawingState);
const auto& parent = mDrawingParent.promote();
// 递归调用 parent 的 isHiddenByPolicy()
if (parent != nullptr && parent->isHiddenByPolicy()) {
return true;
}
// ...
// 检测 Layer 的 hidden 标志位
return s.flags & layer_state_t::eLayerHidden;
}
很显然,app 真窗此时处于 hidden 状态,那么 SurfaceFlinger 发送给 InputDispather 的真窗 Layer 可见性为 false。
恰好,InputDispatcher 中,解析焦点窗口,又需要用到 Layer 可见性,如下
cpp
// FocusResolver.cpp
// token : 是焦点请求的焦点窗口的token
// windows: 所有窗口信息
// outFocusableWindow: 保存最新解析出的焦点窗口
FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
sp<WindowInfoHandle>& outFocusableWindow) {
bool allWindowsAreFocusable = true;
bool windowFound = false;
sp<WindowInfoHandle> visibleWindowHandle = nullptr;
for (const sp<WindowInfoHandle>& window : windows) {
// 过滤非焦点窗口请求的焦点窗口
if (window->getToken() != token) {
continue;
}
// 走到这里,表示焦点窗口请求中的焦点窗口,匹配到窗口信息
windowFound = true;
// 焦点窗口请求中的焦点窗口,必须可见
if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
// Check if at least a single window is visible.
visibleWindowHandle = window;
}
// 焦点窗口请求中的焦点窗口,必须能获得焦点
if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
// Check if all windows with the window token are focusable.
allWindowsAreFocusable = false;
break;
}
}
if (!windowFound) {
return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowHandle) {
return Focusability::NOT_VISIBLE;
}
// Only set the outFoundWindow if the window can be focused
outFocusableWindow = visibleWindowHandle;
return Focusability::OK;
}
因此,InputDispatcher 利用 FocusResolver 解析出的焦点窗口,此时仍然为 null。
当 app 端把真窗绘制完成,并上报 WMS,此时 WMS 会在窗口刷新中,show 真窗 surface,并强制执行一次 update input windows。此时底层才成功地把焦点窗口更新为 app 真窗,上层 WMS 此时会收到真窗改变的回调。