Hiten's Blog.

WindowManager调用流程源码分析

字数统计: 2.4k阅读时长: 12 min
2018/03/30 Share

前两天写Activity启动流程时挖个坑,ActivityThread调用Activity.resume后,紧接着调用WindowManager.addView()用来正在的显示View,之前讲的很草率,现在感觉有必要写一下WindowManager的调用流程。

本文基于android8.1.0_r15分支分析

WindowManager简单介绍

首先WindowManager是一个接口继承自ViewManager,它作用是用于窗口的管理,我们平时的使用比较多的是addView方法和LayoutParams参数,addView定义在基类ViewManager中,源码:

ViewManager

1
2
3
4
5
6
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}

一共三个方法比较简单,注意看参数类型,我们平时用WindowManager时候传的是WindowManager.LayoutParams,这里定义的是ViewGroup.LayoutParams params,我想肯定前者继承后者吧,啥都不说,看看WindowManager.LayoutParams定义;


果不其然,LayoutParams中还定义了一个十分重要的熟悉:type,type代表窗口的类型,系统一共提供三种类型的窗口

  • 应用程序窗口 :顶级窗口
  • 子窗口 :与一个顶级窗口关系的子窗口
  • 系统窗口 :系统窗口,比如toast

常用的type值

WindowManager毕竟只是一个接口,真正的实现在哪里?回想在ActivityThread.handleResumeActivity()中,调用了activity.getWindowManager(),我们定位到Activity:

Activity#getWindowManager

1
2
3
public WindowManager getWindowManager() {
return mWindowManager;
}

在Activity#getSystemService()中同样可以得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
//WINDOW_SERVICE比较特殊,不调用super的
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}

我们注意到在调用getSystemService时,WINDOW_SERVICE比较特殊,没有调用super,直接返回mWindowManager,为什么呢,我们留到后文说;

mWindowManager什么时候赋值的呢,我们在Activity.attach中找到了代码

Activity.attach

1
2
3
4
5
6
7
8
9
10
11
//设置系统的WindowManager
mWindow.setWindowManager(
//获取系统的WindowManager
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
//拿Window对象的
mWindowManager = mWindow.getWindowManager();

也是直接获取系统的,但是经过mWindow转手了,这是为什么呢,Window关于setWindowManager()定义

Window#setWindowManager

1
2
3
4
5
6
7
8
9
10
11
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

正在的实现是WindowManagerImpl,我们查看WindowManagerImpl.addView方法:

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
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
调用mGlobal
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}

private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
if (mDefaultToken != null && mParentWindow == null) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (wparams.token == null) {
wparams.token = mDefaultToken;
}
}
}

方法体首先执行applyDefaultToken,对wparams赋token值,然后直接调用mGlobal对应的方法,mGlobal是WindowManagerGlobal类型,它也继承自WindowManager么?接下来分析WindowManagerGlobal

WindowManagerGlobal

WindowManagerGlobal并不是继承WindowManager,但是有着一样的方法名,这不是代理模式么,它同样有addView、updateViewLayout、removeView方法,我们从addView方法分析

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
73
74
75
76
77
78
79
80
81
82
83
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
//转成WindowManager.LayoutParams对象
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//显示Activity的话window肯定不为空
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
//从mViews集合查找有没有添加过
int index = findViewLocked(view, false);
//有则调用对应的ViewRootImpl.doDie
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//创建ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
//参数最终给了decor
view.setLayoutParams(wparams);
mViews.add(view);//把Decor存起来
mRoots.add(root);//把ViewRootImpl纯起来
mParams.add(wparams);//把参数存起来
// do this last because it fires off messages to start doing things
try {
//设置View
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}

简单分析addView作用:

  1. WindowManagerGlobal中定义了三个List,分别存放传递进来的View、params和生成的ViewRootImpl
  2. 如果判断已经添加过,则调用该view对应的ViewRootImpl.doDie()
  3. 最后执行了root.setView();

继续分析updateViewLayout和removeView方法:

WindowManagerGlobal#updateViewLayout#removeView

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
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
//去除之前的view对应的下标
int index = findViewLocked(view, true);
//缓存的root
ViewRootImpl root = mRoots.get(index);
//mParams集合重新添加
mParams.remove(index);
mParams.add(index, wparams);
//最终调用viewRootImpl的setLayoutParams
root.setLayoutParams(wparams, false);
}
}

public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
//集合里的全都删除掉
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}

private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();

if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
//关掉输入法
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
//调用ViewRootImpl.die
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}

更新和删除的代码比较简单,注释说的很清楚了,只要就是管理集合和调用ViewRootImpl的对应代码

调用顺序总结:

  1. Activity.getWindowManger()获取,返回WindowManagerImpl;
  2. WindowManagerImpl调用WindowManagerGlobal对应方法;
  3. WindowManagerGlobal调用ViewRootImp对应方法;

意义总结:

  1. WindowManagerImpl作为WindowManager接口的默认实现,顺便处理WindowManager.LayoutParams.token
  2. WindowManagerGlobal保存每一次的View、Params、root,防止重复添加,最后调用RootViewImpl实现。

到这里还没有真正的看到IPC调用,因为一层一层代理,到了RootViewImpl这一层,真假美猴王应该能分晓了吧。

RootViewImpl调用IPC

上文分析RootViewImpl在WindowManagerGlobal.addView中构造,并且有三个方法setView()、setLayoutParams()、doDie()被调用,但是看了RootViewImpl代码头发有些发麻,代码量太大了。

由于鄙人水平有些,这篇博客只拿出它和WMS的IPC调用过程分析,这代码只能一片一片剖析。见谅啊!开始吧。

RootViewImpl构造方法

我们发现RootViewImpl构造方法创建了两个window相关的对象,分别是

1
2
3
mWindowSession = WindowManagerGlobal.getWindowSession();

mWindow = new W(this);

跟进WindowManagerGlobal看个究竟

WindowManagerGlobal#getWindowSession#getWindowManagerService

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
//获取WindowSession
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
//通知WMS开启一个新的Session
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}

//获取WindowManagerService
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}

再看W这个类,它继承自IWindow.Stub,如果看过Activity启动流程的应该知道,这个W和ApplicationThread一样的道理啊,我们接着看ViewRootImpl关键是的三个方法有没有mWindowSession和mWindow的调用

ViewRootImpl#setView

1
2
3
4
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);

ViewRootImpl#doDie

1
mWindowSession.finishDrawing(mWindow);

IWindowSession.aild

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface IWindowSession {
int add(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets,
out InputChannel outInputChannel);
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets, out Rect outOutsets, out InputChannel outInputChannel);
int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets);
void remove(IWindow window);

...
}

IWindow.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
oneway interface IWindow {
void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
void closeSystemDialogs(String reason);
...
}

Session#addToDisplay

1
2
3
4
5
6
7
8
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
//调用WindowManagerService的addWindow
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

WindowManagerService#addWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState parentWindow = null;
long origId;
final int callingUid = Binder.getCallingUid();
final int type = attrs.type;
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}

...
}
}

既然涉及到binder双向通信,app端肯定得有handle来处理,果然在ViewRootImpl中定义了ViewRootHandler这个类。

ViewRootHandler

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
final class ViewRootHandler extends Handler {

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
case MSG_INVALIDATE_RECT:
final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
break;
case MSG_PROCESS_INPUT_EVENTS:
mProcessInputEventsScheduled = false;
doProcessInputEvents();
break;
case MSG_DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
case MSG_DISPATCH_GET_NEW_SURFACE:
handleGetNewSurface();
break;
case MSG_RESIZED: {
// Recycled in the fall through...
SomeArgs args = (SomeArgs) msg.obj;
if (mWinFrame.equals(args.arg1)
&& mPendingOverscanInsets.equals(args.arg5)
&& mPendingContentInsets.equals(args.arg2)
&& mPendingStableInsets.equals(args.arg6)
&& mPendingVisibleInsets.equals(args.arg3)
&& mPendingOutsets.equals(args.arg7)
&& mPendingBackDropFrame.equals(args.arg8)
&& args.arg4 == null
&& args.argi1 == 0
&& mDisplay.getDisplayId() == args.argi3) {
break;
}
}
...
}
}

画一张图标识记忆一下吧。

CATALOG