SwallowJoe的博客

Be a real go-getter,
NEVER SETTLE!

0%

WMS(4)-WindowState创建及移除

以下分析基于Android S.

简述

之前分析,Activity在被start时会创建其对应ActivityRecord并保存在对应DisplayContent的mTokenMap中,之后在该Activity被resume时,会通过其对应ViewRootImpl中的setView调用到WMS的addWindow方法,传入参数就是该Activity的ViewRootImpl中的W作为IWindow、存储该Activity窗口属性信息的LayoutParams以及InputChannel被传入WMS,通过这些信息创建WindowState:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
......
// 窗口类型保存在LayoutParams中
final int type = attrs.type;
synchronized (mGlobalLock) {
......
// 这里DisplayContent中至少包含该App的一个WindowToke,也就是Activity被start时创建的ActivityRecord!
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
...
// [1.1] 初始化WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
......
// [1.2] 创建SurfaceSession, 用来和SurfaceFlinger通信
win.attach();
// 保存WindowState, key为ViewRoomImpl.W
mWindowMap.put(client.asBinder(), win);
......
// 将该WindowState保存在其mToken(ActivityRecord)中
win.mToken.addWindow(win);
......
......
}

一. WindowState

WindowState,顾名思义是用于保存窗口状态的类,WindowToke是用于标记窗口身份、对应哪个进程的Activity的类。

4-1

1.1 WindowState的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
super(service);
// 创建Transaction,用于和SurfaceFlinger通信
// mTransactionFactory是实现了Supplier接口的SurfaceControl.Transaction::new
mTmpTransaction = service.mTransactionFactory.get();
// mSession就是ViewRootImpl对应的mWindowSession
mSession = s;
// mClinet就是ViewRootImpl中的mWindow(即W类的对象)
mClient = c;
mAppOp = appOp;
// mToken即WindowToke,也就是该WindowState对应的ActivityRecord
mToken = token;
mActivityRecord = mToken.asActivityRecord();
mOwnerUid = ownerId;
mShowUserId = showUserId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
// 创建WindowId
mWindowId = new WindowId(this);
// 保存该Activity的View对应的窗口属性
mAttrs.copyFrom(a);
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
mViewVisibility = viewVisibility;
// mPolicy就是PhoneWindowPolicy
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
// 创建WindowState所处进程的死亡监听
DeathRecipient deathRecipient = new DeathRecipient();
......
// 创建InputApplicationHandle, 将该Window注册进Input系统,以便后续input事件传输
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
mActivityRecord != null
? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
getDisplayId()));
......
// IWindow(ViewRootImpl.W) 建立binder死亡监听
c.asBinder().linkToDeath(deathRecipient, 0);
......
// 当前 Window type仍旧是TYPE_BASE_APPLICATION
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
......
} else {
// 将baseLayer设置为window类型对应的layer * TYPE_LAYER_MULTIPLIER 再加上一段偏移
// 即 baseLayer = windowLayer * 10000 + 1000;
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
}
// 如果是输入法或者壁纸窗口,则标记为floating layer.
mIsFloatingLayer = mIsImWindow || mIsWallpaper;

if (mActivityRecord != null && mActivityRecord.mShowForAllUsers) {
// 标记可以为所有用户显示的应用窗口可以在锁屏时显示
mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
}

// 创建WindowAnimator,用于实现该窗口的动画
mWinAnimator = new WindowStateAnimator(this);
......
// 非系统进程会有WPC来响应管理该窗口所属进程状态变化
mWpcForDisplayAreaConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
? null
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}

初始化WindowState的过程并不复杂:

  1. 调用SurfaceControl.Transaction::new创建mTmpTransaction
  2. 保存来自应用ViewRootImpl中的mWindowSession、mWindow等
  3. 保存该WindowState对应window的ActivityRecord
  4. 创建WindowId
  5. 保存该Activity的View对应的窗口属性
  6. 创建InputApplicationHandle,即将该Window注册进Input系统,以便后续input事件传输
  7. 建立binder死亡监听,处理该WindowState所属应用进程死亡后资源处理
  8. 计算mBaseLayer,mBaseLayer = windowLayer(2) * 10000 + 1000;
  9. 标记mIsChildWindow、mIsImWindow(输入法窗口)、mIsWallpaper(壁纸窗口)
  10. 创建WindowAnimator,用于实现该窗口的动画
  11. 获取该pid对应的WindowProcessController

这里关于input相关的流程我们后续分析。

1.2 WindowState.attach

1
2
3
4
void attach() {
if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked();
}

很简单的,直接交给mSession处理。

1.2.1 Session.windowAddedLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void windowAddedLocked() {
......
// 第一次进入则需要创建mSurfaceSession
if (mSurfaceSession == null) {
if (DEBUG) {
Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
}
// 创建SurfaceSession
mSurfaceSession = new SurfaceSession();
ProtoLog.i(WM_SHOW_TRANSACTIONS, " NEW SURFACE SESSION %s", mSurfaceSession);
// 新创建的SurfaceSession后,将此Session保存至WMS中的mSessions集合中
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
// 标记窗口数量增一
mNumWindow++;
}

SurfaceSession是用于和SurfaceFlinger通信的,因为Session是单个进程只会持有一个,所以创建SurfaceSession也只会初始化一次。

创建Session的过程:WindowManagerGlobal.getWindowSession

1
2
3
4
5
6
7
8
9
10
11
12
13
>>// WindowManagerGlobal.getWindowSession
>>public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
// 单个进程仅会创建一次
if (sWindowSession == null) {
try {
......
sWindowSession = windowManager.openSession(
......
}
return sWindowSession;
}
>>}

1.2.2 创建SurfaceSession

1
2
3
4
5
6
private static native long nativeCreate();

public SurfaceSession() {
// 通过JNI创建
mNativeClient = nativeCreate();
}

这里直接通过JNI创建对应native层对象,关于JNI后续单独出文章分析,有一个最简单的法则:

SurfaceSession所处的包是 “android.view”, 所以对应JNI文件就是:android_view_SurfaceSession.cpp

1.2.3 android_view_SurfaceSession.cpp:nativeCreate

1
2
3
4
5
6
7
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
// 对应创建的是SurfaceComposerClient
SurfaceComposerClient* client = new SurfaceComposerClient();
// 对象引用计数加一
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}

SurfaceSession对应的native层对象就是SurfaceComposerClient!

1.2.4 创建SurfaceComposerClient

1
2
3
4
SurfaceComposerClient::SurfaceComposerClient()
: mStatus(NO_INIT)
{
}

标记mStatus为NO_INIT,尚未初始化。

回过头看WindowState.attach,这个方法其实就是创建mSurfaceSession并将其保存在WMS.mSessions集合中。而SurfaceSession其实就是对native层SurfaceComposerClient的包装。

1.3 WindowState所属应用进程死亡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void binderDied() {
try {
boolean resetSplitScreenResizing = false;
synchronized (mWmService.mGlobalLock) {
// [1.3.1] 获取WindowState
final WindowState win = mWmService
.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
// 当WindowState尚未被移除时
if (win != null) {
final DisplayContent dc = getDisplayContent();
// 如果该WindowState是对应Activity的,且WindowState的mChildren中类型为TYPE_BASE_APPLICATION的WindowState
// 就是保存在WMS中的WindowState
if (win.mActivityRecord != null && win.mActivityRecord.findMainWindow() == win) {
mWmService.mTaskSnapshotController.onAppDied(win.mActivityRecord);
}
// [1.3.2] 移除该WindowState
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
WindowState.this.removeIfPossible();
}
}
.......
} catch (IllegalArgumentException ex) {
// This will happen if the window has already been removed.
}
}

1.3.1 WMS.windowForClientLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final WindowState windowForClientLocked(Session session, IWindow client, boolean throwOnError) {
return windowForClientLocked(session, client.asBinder(), throwOnError);
}

final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {
WindowState win = mWindowMap.get(client);
if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);
if (win == null) {
......
return null;
}
if (session != null && win.mSession != session) {
......
return null;
}

return win;
}

从WMS的mWindowMap中查找client(ViewRootImpl.W)对应的WindowState, 这个是在addWindow时保存的。

1.3.2 WindowState.removeIfPossible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@Override
void removeIfPossible() {
// WindowState是WindowContainer的子类,这里是对mChilder中的每一个WindowContainer执行removeIfPossible
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}

private void removeIfPossible(boolean keepVisibleDeadWindow) {
// 标记该WindowState是被移除的
mWindowRemovalAllowed = true;
// 应用启动窗口?
final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
......

try {
// 将该WindowState从Input系统中移除
disposeInputChannel();
......

// 标记待移除窗口的可见性
boolean wasVisible = false;

// 首先确认是否需要运行动画。如果需要,必须推迟删除窗口,直到动画完成。如果显示被冻结,只需立即移除,因为不会看到动画。
if (mHasSurface && mToken.okToAnimate()) {
// 该窗口将被替换,只需要标记相关状态,等待被替换完成即可。
if (mWillReplaceWindow) {
......
mAnimatingExit = true;
mReplacingRemoveRequested = true;
return;
}

// 记录待移除的窗口是否可见
wasVisible = isWinVisibleLw();
......
// 如果是可见的,需要创建一个窗口退出动画
if (wasVisible) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;

if (mWinAnimator.applyAnimationLocked(transit, false)) {
mAnimatingExit = true;

// 请求vsync进行动画
setDisplayLayoutNeeded();
mWmService.requestTraversal();
}
......
}
......
}
// [1.3.2.1] 将此WindowState移除
removeImmediately();
// 移除一个可见的窗口可能会影响显示方向,比如横屏游戏,此时需要重新计算更新相关参数
if (wasVisible) {
final DisplayContent displayContent = getDisplayContent();
if (displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
}
// 移除窗口后,计算更新焦点窗口
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
} finally {
Binder.restoreCallingIdentity(origId);
}
}

窗口所属进程死亡后,需要清理该窗口的WindowState:

  1. 标记该WindowState是被移除的,mWindowRemovalAllowed置为true
  2. 将该WindowState从Input系统中移除
  3. 确认是否需要运行动画。如果需要,推迟删除窗口,直到动画完成。如果显示被冻结,只需立即移除,因为不会看到动画。
    1. 如果是可见的,需要创建并执行一个窗口退出动画
  4. 将此WindowState移除
  5. 移除一个可见的窗口可能会影响显示方向,比如横屏游戏,此时需要重新计算更新相关参数
  6. 更新焦点窗口

1.3.2.1 WindowState.removeImmediately - 移除WindowState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
void removeImmediately() {
// 对所有mChildren执行清理动作
super.removeImmediately();
......
final DisplayContent dc = getDisplayContent();
.....
// 将该Window的窗口属性从DisplayContent中移除
dc.getDisplayPolicy().removeWindowLw(this);

disposeInputChannel();
// 移除窗口的Surface
mWinAnimator.destroySurfaceLocked(mTmpTransaction);
mTmpTransaction.apply();
// 将此窗口WindowState从Session中移除
mSession.windowRemovedLocked();
try {
// 解除binder死亡监听
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
} catch (RuntimeException e) {
}
// 执行清理后操作,从WMS.mWindowMap中移除该WindowState然后更新绘制
mWmService.postWindowRemoveCleanupLocked(this);
}

移除WindowState会执行:

  1. 对所有mChildren执行清理动作
  2. 将该WindowState的窗口属性从DisplayContent中移除
  3. 移除窗口的Surface
  4. 将此窗口WindowState从Session中移除
    1. Session中窗口数目减一
    2. 如果窗口数量大于0或者对应应用进程没有死亡时,不需要移除session,以备后续使用
    3. 否则将此Session从WMS.mSessions集合中移除
    4. 如果存在mSurfaceSession,则同步清理,清理时将该SurfaceSession对应的SurfaceComposerClient的引用计数减掉
  5. 执行清理后操作,从WMS.mWindowMap中移除该WindowState然后更新绘制

1.3.2.2 Session.windowRemovedLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void windowRemovedLocked() {
// 窗口数目减一
mNumWindow--;
killSessionLocked();
}

private void killSessionLocked() {
// 如果窗口数量大于0或者对应应用进程没有死亡时,不需要移除session,以备后续使用
if (mNumWindow > 0 || !mClientDead) {
return;
}
// 将此Session从WMS.mSessions集合中移除
mService.mSessions.remove(this);
// 如果存在mSurfaceSession,则同步清理
if (mSurfaceSession == null) {
return;
}
......
try {
// 不用怀疑,肯定是需要进入native层执行清理动作
mSurfaceSession.kill();
} catch (Exception e) {
Slog.w(TAG_WM, "Exception thrown when killing surface session " + mSurfaceSession
+ " in session " + this + ": " + e.toString());
}
mSurfaceSession = null;
mAlertWindowSurfaces.clear();
mAppOverlaySurfaces.clear();
setHasOverlayUi(false);
cancelAlertWindowNotification();
}

1.3.2.3 SurfaceSession.kill - 清理SurfaceSession

1
2
3
4
5
6
public void kill() {
if (mNativeClient != 0) {
nativeDestroy(mNativeClient);
mNativeClient = 0;
}
}

执行native清理动作,将mNativeClient置为0.

1
2
3
4
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
}

native层的清理动作也很简单,将该SurfaceSession对应的SurfaceComposerClient的引用计数减掉即可。

二. 小结

回顾下本文涉及类的类图:

4-2

之前分析,Activity在被start时会创建其对应ActivityRecord并保存在对应DisplayContent的mTokenMap中,之后在该Activity被resume时,会通过其对应ViewRootImpl中的setView调用到WMS的addWindow方法,传入参数就是该Activity的ViewRootImpl中的W作为IWindow、存储该Activity窗口属性信息的LayoutParams以及InputChannel被传入WMS,通过这些信息创建WindowState:

  1. 初始化WindowState
    1. 调用SurfaceControl.Transaction::new创建mTmpTransaction
    2. 保存来自应用ViewRootImpl中的mWindowSession、mWindow等
    3. 保存该WindowState对应window的ActivityRecord
    4. 创建WindowId
    5. 保存该Activity的View对应的窗口属性
    6. 创建InputApplicationHandle,即将该Window注册进Input系统,以便后续input事件传输
    7. 建立binder死亡监听,处理该WindowState所属应用进程死亡后资源处理
    8. 计算mBaseLayer,mBaseLayer = windowLayer(2) * 10000 + 1000;
    9. 标记mIsChildWindow、mIsImWindow(输入法窗口)、mIsWallpaper(壁纸窗口)
    10. 创建WindowAnimator,用于实现该窗口的动画
    11. 获取该pid对应的WindowProcessController
  2. WindowState.attach, 创建SurfaceSession
    1. SurfaceSession是用于和SurfaceFlinger通信的,因为Session是单个进程只会持有一个,所以创建SurfaceSession也只会初始化一次。
    2. 创建SurfaceSession就是创建其对应native层的对象SurfaceComposerClient, 将其mStatus标记为NO_INIT,尚未初始化
  3. 保存WindowState至WMS.mTokenMap中, key为ViewRoomImpl.W
  4. 将该WindowState保存在其mToken(ActivityRecord的mChildren)中

当WindowState对应的进程死亡时,执行相关清理动作:

  1. 标记该WindowState是被移除的,mWindowRemovalAllowed置为true
  2. 将该WindowState从Input系统中移除
  3. 确认是否需要运行动画。如果需要,推迟删除窗口,直到动画完成。如果显示被冻结,只需立即移除,因为不会看到动画。
    1. 如果是可见的,需要创建并执行一个窗口退出动画
  4. 将此WindowState移除
    1. 对其所有mChildren执行清理动作
    2. 将该WindowState的窗口属性从DisplayContent中移除
    3. 移除窗口的Surface
    4. 将此窗口WindowState从Session中移除
      1. Session中窗口数目减一
      2. 如果窗口数量大于0或者对应应用进程没有死亡时,不需要移除session,以备后续使用
      3. 否则将此Session从WMS.mSessions集合中移除
      4. 如果存在mSurfaceSession,则同步清理,清理时将该SurfaceSession对应的SurfaceComposerClient的引用计数减掉
    5. 执行清理后操作,从WMS.mWindowMap中移除该WindowState然后更新绘制
  5. 移除一个可见的窗口可能会影响显示方向,比如横屏游戏,此时需要重新计算更新相关参数
  6. 更新焦点窗口

这里我们注意到WindowState被销毁时会存在一个窗口动画,那么当WindowState的窗口创建时应该也存在一个窗口动画,对应的应该是我们在WindowState被销毁时的TYPE_APPLICATION_STARTING类型窗口(因为Activity对应窗口尚未创建完毕,那么窗口启动动画应该在另一个准备完毕的窗口执行才行)。

下一篇文章我们就探秘一下应用启动窗口动画。

欢迎关注我的其它发布渠道