SwallowJoe的博客

Be a real go-getter,
NEVER SETTLE!

0%

WMS(1)-服务启动和Activity窗口回顾

以下分析基于Android S.

简述

之前我们通过Vsync这个Android绘制的脉搏疏通了绘制的流程,在Activity的显示研究过程中,粗略接触了WindowManagerService,也即Android的窗口管理。

接下来我们深入探讨一下Android中的窗口管理设计,以便我们理解View、Activity、Window、Task等之间的关系。

首先还是从WindowManagerService服务的启动开始,在Android开机流程中,我们知道WMS是在引导服务和核心服务启动之后才会开始的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
t.traceBegin("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
// 1.1 WMS的初始化
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
// 发布WMS
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
t.traceEnd();

t.traceBegin("SetWindowManagerService");
mActivityManagerService.setWindowManager(wm);
t.traceEnd();

t.traceBegin("WindowManagerServiceOnInitReady");
wm.onInitReady();
t.traceEnd();
}

这里眼尖的同学发现了,WMS的启动必须等待SensorService的启动完毕,为什么呢,后面给出答案。

一. WMS启动

从上面也看出了,WMS的启动是先调用了其静态方法main, 并传入context, inputManager及ActivityTaskManager等,还有一个WindowManagerPolicy子类PhoneWindowManager的对象.

1.1 WindowManagerService.main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
return main(context, im, showBootMsgs, onlyCore, policy, atm,
SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new);
}

/**
* Creates and returns an instance of the WindowManagerService. This call allows the caller
* to override factories that can be used to stub native calls during test.
*/
@VisibleForTesting
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
// 使用"android.display"线程初始化WindowManagerService
// 初始化没有结束时,调用main的线程会被阻塞
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
return sInstance;
}

这里使用的双冒号”::”、Supplier以及Function都是java8引入的函数式编程语法。这里值得注意的是runWithScissors方法。

1.2 DisplayThread.getHandler

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
public final class DisplayThread extends ServiceThread {
private static DisplayThread sInstance;
private static Handler sHandler;

private DisplayThread() {
// DisplayThread runs important stuff, but these are not as important as things running in
// AnimationThread. Thus, set the priority to one lower.
super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);
}

private static void ensureThreadLocked() {
if (sInstance == null) {
sInstance = new DisplayThread();
sInstance.start();
sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
sHandler = new Handler(sInstance.getLooper());
}
}

public static Handler getHandler() {
synchronized (DisplayThread.class) {
ensureThreadLocked();
return sHandler;
}
}
...
}

由此可以看出DisplayThread其实就是Android里的”android.display”线程,优先级仅仅比THREAD_PRIORITY_DISPLAY低一档。

1.2.1 Handler.runWithScissors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @hide
*/
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}

if (Looper.myLooper() == mLooper) {
r.run();
return true;
}

BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}

这里可以看到该方法首先检查了参数合法性,然后判断当前允许的线程是否是Handler处理线程:

  1. 如果是Handler处理线程,则执行runnable
  2. 如果不是,则创建一个BlockingRunnable,执行其postAndWait方法

从postAndWait名字中可以推测,必定是需要等待指定runnable在Handler处理线程上执行完毕后,调用runWithScissors的线程才能继续运行。

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
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;

public BlockingRunnable(Runnable task) {
mTask = task;
}

@Override
public void run() {
try {
mTask.run();
} finally {
// runnable执行完毕后标记mDone为true,然后通知所以等待线程
synchronized (this) {
mDone = true;
notifyAll();
}
}
}

public boolean postAndWait(Handler handler, long timeout) {
// 当handler中已经存在此runnable时,返回false
if (!handler.post(this)) {
return false;
}

synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
// 等待delay时长
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}

所以runWithScissors方法为啥会标记为hide呢,明明很好的实现了将某个任务放到指定线程执行并等待该任务执行完毕后继续。

  1. 考虑到如果等待超时,postAndWait返回的也是false,但是对应runnable的message仍旧处于Handler的MessageQueue之中,这样该runnable最终还是会被执行的。
  2. 可能造成死锁,因为runWithScissors所处线程会一直等待,除非超时。比如任务所处的handler的Looper被调用了quit()退出时。我们知道quit方法是会清理Looper的MessageQueue中所有消息,如果此时timeout的时间设置的是0,那么runWithScissors方法所处的线程会一直等到天荒地老。除非Looper退出时是调用的quitSafely(), 该方法只会清空MessageQueue中当前时间点之后的message,在这之前的message仍会被执行。

而我们知道,DisplayThread代表线程是不会退出的(除非关机), 所以这里调用runWithScissors是安全的。

1.3 WindowManagerService

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
72
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
// 在LockGuard中创建代表WINDOW的Lock, 可以帮助检测系统服务内部锁的机制
installLock(this, INDEX_WINDOW);
...
// 标记当前设备是否是可触摸的
mInTouchMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
// 设置是否允许触摸,现在手机、平板设备一般都是可触摸的
inputManager.setInTouchMode(mInTouchMode);
...
// 通过surfaceControlFactory可以直接生成SurfaceControl.Builder的对象
mSurfaceControlFactory = surfaceControlFactory;
// 其实是实现了Supplier接口的SurfaceControl.Transaction::new
mTransactionFactory = transactionFactory;
// 其实是实现了Supplier接口Surface::new
mSurfaceFactory = surfaceFactory;
// 创建Transaction对象, 注意每次调用get方法生成的都是新的SurfaceControl.Transaction对象
mTransaction = mTransactionFactory.get();
// 这个policy就是PhoneWindowPolicy
mPolicy = policy;
// 创建WindowAnimator
mAnimator = new WindowAnimator(this);
// 创建RootWindowContainer
mRoot = new RootWindowContainer(this);

...
// 創建WindowSurfacePlacer,如其名,用於定位窗口及其表面
// 通过计算窗口的框架来设置窗口的位置,然后根据这些框架来定位Surface
mWindowPlacerLocked = new WindowSurfacePlacer(this);
// 創建TaskSnapshotController,用於获取相应任务的快照(位图)并将其放入缓存
mTaskSnapshotController = new TaskSnapshotController(this);

...
// 创建WakeLock,用于在转屏等动作时保持cpu不休眠
mScreenFrozenLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
// 用于管理监听Display的层次属性变化的监听器
mDisplayNotificationController = new DisplayWindowListenerController(this);
...

// 创建WakeLock,在必要时保持屏幕常量
mHoldingScreenWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
mHoldingScreenWakeLock.setReferenceCounted(false);
// 用于执行不需要持有WindowManager的锁的窗口动画
mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory,
mPowerManagerInternal);

...
// 用于任务拖动定位的控制器
mTaskPositioningController = new TaskPositioningController(
this, mInputManager, mActivityTaskManager, mH.getLooper());
// 该类处理并组合从多个视图中生成的拖动事件,然后向任何已经注册了回调的ondragdroplistener触发事件。
mDragDropController = new DragDropController(this, mH.getLooper());
// 用于强制显示高刷新率的包的Denylist
mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());
...
// 创建DisplayAreaPolicyProvider,用于后续的DisplayAreaPolicy的构建
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
...
// 设置全局阴影
setGlobalShadowSettings();
mAnrController = new AnrController(this);
// 管理创建和释放启动窗口Surface
mStartingSurfaceController = new StartingSurfaceController(this);
}

这里省略的很多设置代码,仅贴了一些比较重要或者有趣的类对象初始化。

二. Activity的Window创建

之前我们在*Activity的显示(1)*一文中有分析过,应用的Activity在onCreate方法中setContentView设置了UI布局,之后在该Activity第一次resume时,初始化ViewRootImpl,当该Activity是应用进程第一个被resume的Activity时,建立进程与WMS的通信通道,也就是WindowSession!

最后ViewRootImpl的setView方法中,通过binder调用WindowManager的addToDisplay,之后进入到SystemServer进程调用到WMS.addWindow,在这里创建该Activity对应的WindowState,并保存在mWindowMap中。

如下即是WMS中addWindow的关键代码:

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
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
......
synchronized (mGlobalLock) {
......
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
......
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;

final IBinder windowContextToken = attrs.mWindowContextToken;
if (token == null) {
if (hasParent) {
......
} else {
// 一般走这里创建WindowToken
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
}
}
...
// 初始化WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
......
// 创建SurfaceSession, 用来和SurfaceFlinger通信
win.attach();
// 保存WindowState
mWindowMap.put(client.asBinder(), win);
......
win.mToken.addWindow(win);
......
......
}

如上,我们可以看到WMS中几个关键的类: WindowState,WindowToken,ActivityRecord等。之前我们就绘制过这个类图:

1-1

  1. WindowToken: 是在WindowManagerService 中定义的一个基类,顾名思义,它是用来标识某一个窗口。可以把WindowToken看成是一个显示令牌,无论是系统窗口还是应用窗口,添加新的窗口时需要使用这个令牌向WMS表明自己的身份,添加窗口(addWindow)时会创建WindowToken,销毁窗口的时候移除WindowToken(removeWindowToken方法)。
  2. ActivityRecord(原AppWindowToken): 顾名思义,它是用来标识app, 跟准确的说法,是用来标识某个具体的Activity. App每个的Activity对应一個ActivityRecord。其中的appToken為IApplicationToken.Stub类型,有了它就可以找到对应的ActivityRecord.

从上面的关系图也能发现,这个WindowToken中token是关键点,我们尚未理清这个值的含义,接下来我们分析一下这个token的来源:

2.1 PhoneWindow的创建

简单回顾一下 Activity 对应的 PhoneWindow的创建,注意这里仅仅是App进程中的Window.

我们从handleLaunchActivity开始:

1
2
3
4
5
6
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
......
final Activity a = performLaunchActivity(r, customIntent);
......
}

2.1.1 performLaunchActivity

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
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
......
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 通过反射生成Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
}
try {
// 创建应用Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
......
if (activity != null) {
......
// 这里传入的window一般是null的
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
......
synchronized (mResourcesManager) {
// 将该Activity保存至mActivities集合中,key值就是ActivityClientRecord中的token
mActivities.put(r.token, r);
}
}
......
}

每次调用performLaunchActivity就一定会创建一个新的Activity对象,并保存在mActivities集合中。

2.1.2 Activity.attach

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
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
......
// 创建Activity对应的PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
......
// 设置PhoneWindow的WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
// 当Activity的parent不为空时,该Activity的PhoneWindow对应的容器就是其parent对应的窗口
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
......
}

可以看到一个Activity对应一个PhoneWindow, 而且PhoneWindow是在Activity launch的时候才创建的。

2.2 WindowToken中token的作用

我们假设添加window的Activity是该应用第一个被resume的activity:

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
//WMS.addWindow函数中:
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);

// WindowToken.java
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
roundedCornerOverlay, false /* fromClientToken */, null /* options */);
}

WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
}

从WindowToken的构造函数和其初始化的传参中可以看到,WindowToken中的token就是attrs的token,或者是client(IWindow)转换来的。

2.2.1 LayoutParams中的token

溯源addWindow, 在ViewRootImpl.setView时,会将该LayoutParams作为参数传入,注意,有两个LayoutParams,一个是WindowManager.LayoutParams, 一个是其父类ViewGroup.LayoutParams:

1_2

而ViewRootImpl.setView也是在 WindowManagerGlobal.addView 时调用的,也就是ActivityThread处理该Activity的Resume状态时:

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
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
......
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
// 现将该View设置为不可见
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
// 从这里拿到的params, 所以token也需要查一下这个LayoutParams
// 最后可以发现是在PhoneWindow创建时候创建的默认值,token还是null的
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// 注意这里type表示为应用窗口
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 这里的wm是Activity中保存的WindowManager, 是该Activity被创建
// 后调用其attach函数创建Window时的WindowManagerImpl,这一部分我
// 们在Activity的显示(1)中有分析
wm.addView(decor, l);
......
......
......
}

从上可以看出只有当该Activity的window是尚未被添加且此次resume状态时是需要可见的时候,才会将该Activity对应View添加至WindowManager中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// WindowManagerImpl.addView
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
// 这里的WMI是在Activity调用attach时通过createLocalWindowManager创建的
// mParentWindow就是创建该WMI的window,即Activity里的PhoneWindow
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}

private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// Only use the default token if we don't have a parent window and a token.
if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
// token在此赋值吗?其实一般不是的,这个mDefaultToken只有AccessibilityService中会设置
// 所以mDefaultToken还是null的
wparams.token = mDefaultToken;
}
wparams.mWindowContextToken = mWindowContextToken;
}

虽然不是在此追踪,但是我们可以发现mGlobal.addView中的LayoutParams仍然是ViewGroup.LayoutParams类型,其并不带有token成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
......
// 因为WindowManager.LayoutParams是ViewGroup.LayoutParams子类,所以可以强制转换
// 但是token默认是null的
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// parentWindow一般不为null,如果是ApplicationType的window,这就是resume的Activity里的PhoneWindow自身
if (parentWindow != null) {
// 调整 WindowManager.LayoutParams
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
......
synchronized (mLock) {
// 2.3 创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
......
root.setView(view, wparams, panelParentView, userId);
......
}
}

注意每个Activity对应一个PhoneWindow, 一个PhoneWindow对应一个WindowManager(即WindowManagerImpl), 而PhoneWindow继承自Window。PhoneWindow在其对应的Activity被调用attach时创建, 然后会创建WindowManagerImpl,保存在PhoneWindow的mWindowManager成员中。

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
// Window.java
// Activity.attach -> PhoneWindow.setWindowManager->
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
// 这个就是Activity中的 mToken, 对应的是LocalActivityManager.LocalActivityRecord
// 顺便说一下这个token是在startActivity时创建的
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

// ActivityThread.handleResumeActivity->WindowManagerImpl.addView->WindowManagerGlobal.addView->
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
// 在handleResumeActivity我们可以看到这个type被指明为TYPE_BASE_APPLICATION
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
......
} else {
// 到目前为止,token确实是null的
if (wp.token == null) {
// mContainer是包含此窗户的容器,没有设置时,DecorWindow将作为顶级窗口
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
......
......
}

一般Window的mContainer是null的,所以wp.token就是Window的mAppToken,也即Activity中的mToken, 对应LocalActivityManager.LocalActivityRecord, 通过此变量可以快速找到Activity、ActivityInfo、Window等。所以最后Activity对应的WindowState中对应的WindowToken里的token成员变量就是Activity中的mToken.

1-3

2.3 Activity的View与Window的关系

在应用的一个Activity对象即将resume时,会通过WindowManagerImpl将View与该Activity对应的PhoneWindow关联起来。这部分发生WindowManagerGlobal.addView中:

1
2
3
4
5
6
7
8
9
10
11
12
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
......
synchronized (mLock) {
// 创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
......
root.setView(view, wparams, panelParentView, userId);
......
}
}

这里传入的View 就是 r.window.getDecorView() 获取的decorView; 而parentWindow就是该Activity首次launch时创建的PhoneWindow(如果没有parent Activity时)

简单回顾下 PhoneWindow 的 DecorView是怎么来的:

  1. 在Activity被launch后,会调用其onCreate方法
  2. 应用会在这个方法中调用setContentView, 将该Activity对应的View或者layout资源ID传入
  3. 然后在调用该Activity对应PhoneWindow的setContentView方法
    1. 如果是首次调用,则先调用installDecor方法创建DecorView
    2. 之后通过PhoneWindow中的mLayoutInflater将该View实例化
    3. 最后将该View保存在PhoneWindow的ViewGroup对象mContentParent中

这部分的流程比如installDecor方法我们之后讨论View的生成时再详细分析。所以简单来说,Activity对应的layout视图作为一个子视图保存在这个Activity对应PhoneWindow的DecorView中。

2.4 IWindowSession - PhoneWindow与WMS的交互

Activity对应的Window和WMS的交互肯定是双向的,那么:

  1. Activity对应的PhoneWindow需要保存WMS的client端
  2. WMS需要保存该PhoneWindow的client端

毫无疑问,PhoneWindow中保存WMS的client端就是对应的IWindowManager, 保存在WindowManagerGlobal中。而PhoneWindow会用在其对应的ViewRootImpl中继承了IWindow.Stub的W类的对象,通过binder传递到WMS中,作为PhoneWindow在WMS中的client端:

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
// ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}

public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
......
// 创建W对象
mWindow = new W(this);
......
}

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
.......
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
mTempControls);
......
}

1-4

由此可见,ViewRootImpl发挥这至关重要的作用,其不仅包含Activity对应的View, PhoneWindow及该Window的属性LayoutParams, 还保存着作为WMS中该Activity的Window的服务端W类的对象。

之前我们略有分析mWindowSession的创建,这里我们直接给出时序图:

1-5

之前我们也说过:

  1. IWindowManager 对应WindowManagerService, 应用进程端通过这个接口即可向WMS传递消息.
  2. IWindowSession 通过这个Session和WMS中的Session交互.暂时还不理解这个作用
  3. W extends IWindow 对应应用进程端的窗口, WMS通过这个向应用进程的Window发送消息.

现在我们分析IWindowSession的方法其实不难理解,IWindowSession是为了方便管理某个应用的某个Window与WMS通信的,虽然最后也是与WMS通信,但是将通信过程也视为一个对象,方便了管理。

三. 小结

本文中分析了Android显示框架中Framework层最重要的WMS服务启动过程,以及复习了Activity窗口的创建过程。

3.1 WMS服务启动

  1. WMS服务是在引导服务和核心服务启动之后启动的,而且必须等待Sensor服务的启动完毕。
  2. WMS服务的启动首先初始化了”android.display”线程,然后将启动过程放在此线程中处理,主线程阻塞等待该启动完毕。
  3. WMS服务的创建过程:
    1. 创建了WindowAnimator
    2. 创建RootWindowContainer
    3. 创建WindowSurfacePlacer,如其名,用於定位窗口及其表面
    4. 创建TaskSnapshotController,用於获取相应任务的快照(位图)并将其放入缓存
    5. 设置全局阴影等等
  4. WMS服务创建完毕后,通过ServiceManager将其发布,服务名为Context.WINDOW_SERVICE

3.2 Activity、Window与WindowManagerService

  1. Activity在第一次启动后,调用attach方法时,会创建PhoneWindow以及对应的WindowManagerImpl
  2. 在Activity第一次resume时,调用该Activity的WindowManagerImpl创建ViewRootImpl
    1. 在创建ViewRootImpl之前,会通过adjustLayoutParamsForSubWindow将该Activity的mToken保存在PhoneWindow的LayoutParams中
    2. 创建ViewRootImpl过程中,会通过IWindowManager与WMS通信,创建IWindowSession, 用于该Activity的Window和WMS通信
  3. 创建ViewRootImpl之后,再通过期间创建的IWindowSession与将该Activity的IWindow、相关Window属性传递给WMS.

接下来我们看看WMS是怎么管理不同应用的Window的。

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