Android:窗口管理

5.jpg

Android的窗口管理如图所示!

这里我也不说Window了,我直接就说PhoneWindow了,因为不管是Activity还是Dialog他们的Window都是PhoneWindow.

每个PhoneWindow都对应一个WindowManagerImpl,通过Window.setWindowManager()方法进行WindowManagerImpl的创建. 拿Acvitity的创建来说,在ActivityThreadperformLaunchActivity时候,调用了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
class Activity{
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) {
// 第一行
mWindow = new PhoneWindow(this, window, activityConfigCallback);
// 第二行
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

}
}

在 第一行 创建了 PhoneWindow对象,然后在 第二行 给当前 PhoneWindow 设置了 WindowManagerImpl 对象, 看下创建过程:

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
class Activity{
void attach(){
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
}
}
class PhoneWindow{
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//创建了属于自己的 windowManagerImpl,并且将自己传入进 WindowManagerImpl
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
}

class WindowManagerImpl{
// 标记1
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}
}

在调用setWindowManager的时候一定要传入一个WindowManager对象,在我看来,比如Activity的启动,这个…..然后比如Dialog,这个WindowManager就是当前触发Dialog显示的那个Activity的WindowManagerImpl.

这里需要着重看下 标记1 的地方, parentwindow是什么,他为什么如此重要,就像他的名字一样 父窗口, 每个WindowManagerImpl里面都保留了当前的PhoneWindow对象,目的是在addView的时候,就可以知道新添加的Window需要指定到哪个 windowToken 上.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class WindowManagerGlobal{
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// 给新的window设置 windowToken 值, 值的由来就是用过 parentWindow获取到的
parentWindow.adjustLayoutParamsForSubWindow(wparams);
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView, userId);
}
}
}

现在我们来看下一个Window是怎么被添加进去和管理的:

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
class ViewRootImpl{
void setView(){
// 当调用到来这里就切换到了系统进程,进入到了 windowManagerService
int res = mWindowSession.addToDisplayAsUser(mWindow,mWindowAttributes,getHostVisibility(),mDisplay.getDisplayId(),userId,..)
}
}

class Session{
//这时候就来到了系统进程
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
// 这个mService 就是 WindowManagerService
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}
}

class WindowManagerService{
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {

// 可以看到这里获取到了 DisplayContent对象
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
//看到没,通过windToken管理着一组windowState
win.mToken.addWindow(win);
}

private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
if (token != null) {
//可以看到去获取对应的 WindowToken
//这时候这里的mRoot对象是 RootWindowContainer.java
// 标记1
final WindowToken wToken = mRoot.getWindowToken(token);
if (wToken != null) {
return wToken.getDisplayContent();
}
}

return mRoot.getDisplayContentOrCreate(displayId);
}
}

class RootWindowContainer{
WindowToken getWindowToken(IBinder binder) {
//标记2
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
final WindowToken wtoken = dc.getWindowToken(binder);
if (wtoken != null) {
return wtoken;
}
}
return null;
}
}

现在看下标记1 标记2这时候的内存分布情况:

图1
图2
图3

看了这三张图你就能深刻理解到最上面图的意思了. 首先 RootWindowContainer管理着所有的DisplayContent,一个DisplayContent代表一个屏幕.然后一个屏幕管理着所有的WindowToken,一个WindowToken管理这一组窗口,比如,两个Activity,他们的WindowToken一定是不一样的,如果是在Activity里面启动了一个Dialog,那么这两个窗口的WindowToken就是一样的.