SystemUI(三)扒一扒WindowManager的addView

从上面两篇SystemUI中发现StatusBar是通过WindowManager的addView()添加在界面上,那么内部Window是如何进行添加的?

带着疑问,跟踪到WindowManager.addView()方法中,由于WindowManager是一个接口,而实现它的是WindowManagerImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Window mParentWindow;
...

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
...

}

在WindowManagerImpl可以发现,最终进行addView操作的是WindowManagerGlobal这个类,接着跟踪进去,可以发现重头戏在WindowManagerGlobal这个类中,那么接下来就开始理清内部逻辑。

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
      public void addView(View view, ViewGroup.LayoutParams params,
279 Display display, Window parentWindow) {
280 if (view == null) {
281 throw new IllegalArgumentException("view must not be null");
282 }
283 if (display == null) {
284 throw new IllegalArgumentException("display must not be null");
285 }
286 if (!(params instanceof WindowManager.LayoutParams)) {
287 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
288 }
289
290 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
291 if (parentWindow != null) {
292 parentWindow.adjustLayoutParamsForSubWindow(wparams);
293 } else {
294 // If there's no parent, then hardware acceleration for this view is
295 // set from the application's hardware acceleration setting.
296 final Context context = view.getContext();
297 if (context != null
298 && (context.getApplicationInfo().flags
299 & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
300 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
301 }
302 }
303
304 ViewRootImpl root;
305 View panelParentView = null;
306
307 synchronized (mLock) {
308 // Start watching for system property changes.
309 if (mSystemPropertyUpdater == null) {
310 mSystemPropertyUpdater = new Runnable() {
311 @Override public void run() {
312 synchronized (mLock) {
313 for (int i = mRoots.size() - 1; i >= 0; --i) {
314 mRoots.get(i).loadSystemProperties();
315 }
316 }
317 }
318 };
319 SystemProperties.addChangeCallback(mSystemPropertyUpdater);
320 }
321
322 int index = findViewLocked(view, false);
323 if (index >= 0) {
324 if (mDyingViews.contains(view)) {
325 // Don't wait for MSG_DIE to make it's way through root's queue.
326 mRoots.get(index).doDie();
327 } else {
328 throw new IllegalStateException("View " + view
329 + " has already been added to the window manager.");
330 }
331 // The previous removeView() had not completed executing. Now it has.
332 }
333
334 // If this is a panel window, then find the window it is being
335 // attached to for future reference.
336 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
337 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
338 final int count = mViews.size();
339 for (int i = 0; i < count; i++) {
340 if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
341 panelParentView = mViews.get(i);
342 }
343 }
344 }
345
346 ...
365 }

在最开始280行-300行,对传进来的参数进行检查,view,display为null,params不属于WindomManager.LayoutParams都会抛出异常。另外如果是子Window,那么还需要通过adjustLayoutParamsForSubWindow(wparams)对布局参数进行调整。

在对参数进行检查后,首先通过findViewLocked查找到当前View的索引,并且判断当前view是否包含在mDyingViews(一个存储待删除的View的List集合)中,如果包含其中,则不等待MSG_DIE消息,直接调用doDie根据获取的索引进行删除。否则抛出异常。

在336处,如果是papanel window,则遍历所有View,获取papanel window所连接的view。准备工作已经做好,接下来就Window进行添加操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
       			root = new ViewRootImpl(view.getContext(), display);
347
348 view.setLayoutParams(wparams);
349
350 mViews.add(view);
351 mRoots.add(root);
352 mParams.add(wparams);
353
354 // do this last because it fires off messages to start doing things
355 try {
356 root.setView(view, wparams, panelParentView);
357 } catch (RuntimeException e) {
358 // BadTokenException or InvalidDisplayException, clean up.
359 if (index >= 0) {
360 removeViewLocked(index, true);
361 }
362 throw e;
363 }
364 }

在上述代码的最后,通过ViewRootImpl.setView来更新界面.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
...
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
try {
755 mOrigWindowType = mWindowAttributes.type;
756 mAttachInfo.mRecomputeGlobalAttributes = true;
757 collectViewAttributes();
758 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
759 getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
760 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
761 mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
762 } catch (RemoteException e) {
...
}
...
}

758行,在setView方法中主要调用了mWindowSession的addToDisplay方法。mWindowSession属于IWindowSession类型,一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,它的Server端的实现为Session,此前包含ViewRootImpl在内的代码逻辑都是运行在本地进程的,而Session的addToDisplay方法则运行在WMS所在的进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
...
@Override
201 public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
202 int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
203 Rect outStableInsets, Rect outOutsets,
204 DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
205 return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
206 outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
...
207 }
}

到了最后,可以发现addView其实是WindowManagerService进行addWindow操作。

Window的添加操作实际上是先通过WindowManagerGlobal对View进行第一步的处理,ViewRootImpl来进行界面的刷新,内部则通过Session与WindowManagerService进行跨进程通信,将添加的请求交给WindowManagerService来处理。