全面了解Window

前置知识

  1. Window是什么?Window是一个组件,View是由Window呈现出来的。Window实际上就是管理着View,对Window的操作最终都会转化成对View的操作。
  2. 经常使用的WindowActivityDialogPopupWindowToast等。系统中常见的WindowStatusBarNavigationBarInputMethod(软键盘)等。StatusBarNavigationBar等是在单独的进程中使用的。

问题

1.WindowManager.LayoutParam(继承ViewGroup.LayoutParams)类很重要,flagstype 等等。

type,相当于Window 的类型,主要分为3大类。

  • 1-99对应应用Window(token 必须设置为Activity 的token,即Context需要设置为Activity)。Activity 中的Window 对应的type 是TYPE_BASE_APPLICATIONDialog默认的是TYPE_APPLICATIONWindowManager.LayoutParams默认构造函对应的type也是TYPE_APPLICATION
  • 1000-1999对应子Window。PopupWindow 默认是TYPE_APPLICATION_PANEL
  • 2000-2999 对应的是系统Window。Toast对应TYPE_TOAST,StatusBar对应TYPE_STATUS_BARNavigationBar对应TYPE_NAVIGATION_BAR,键盘对应着”TYPE_INPUT_METHOD”。

flags,各种不一样属性,控制Window 的一些特殊显示。

2.DialogPopupWindow的异同。

  • DialogPopupWindoow 都会在一个新的Window中展示。
  • Dialog不能使用ApplicationContext,上面就可以知道Dailog是应用Window,只能使用Activity
  • PopupWindow不能在Activity.onCreate中创建。PopupWindow默认是一个子Window,需要在Activity创建以后使用。
  • PopupWindow一定需要设置widthheight,默认是0。
  • Dialog是创建了一个PhoneWindow然后获取的DecorView,所有默认有Title,而PopupWindow没有。
  • PopupWindow默认不会响应Back键,可以设置popupWindow.setFocusable(true);
  • 通过Activity来管理Dialog的时候,Activity.mManagedDialogs,当Activity后台销毁的时候再次进入可以恢复(参考Dialog.onSaveInstanceStateDialog.onRestoreInstanceState)。而PopupWindow没有恢复机制。

至于网上很多说PopupWindow是阻塞式的而Dialog是非阻塞式的,是非常误解人的。关于坑爹的PopupWindow的“阻塞”争议问题:Android没有真正的“阻塞式”对话框

3.Toast展示以后可以将Toast点击的事件传递到下面的View,Dialog以及Popupindow都不行,跟WindowManager.LayoutParams.flags有关。

4.Activity设置windowSoftInputMode属性的时候,会对ActivityWindow有一些影响。

5.一个App 中有多少个Window?应该是某个状态下Window的个数,Activity+Dialog+PopupWindow+Toast+WindowManage.addView

基本操作

WindowManager继承ViewManager,基本的操作如下。操作的实际上是ViewWindowManagerImpl保存着一族对应的mViewsmRootsmParams。对于每一个展示的Window实际上对应着mViews[k]mRoots[k]mParams[k]。具体可以看后面的流程分析。

1
2
3
4
// ViewManager.java
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);

流程分析

Window.setStatusBarColor流程(api 21以上)。

StatusBarNavigationBar都是一个View展示在最上面和最下面??

1.Window.setStatusBarColor`。

1
2
3
4
5
6
7
8
9
10
// PhoneWindow.java
@Override
public void setStatusBarColor(int color) {
mStatusBarColor = color;
mForcedStatusBarColor = true;
if (mDecor != null) {
// 直接调用到了DecorView.updateColorViews()
mDecor.updateColorViews(null, false /* animate */);
}
}

2.DecorView.updateColorViews。从代码中看就是修改了mNavigationColorViewStatemStatusColorViewState里面包含的ViewbackgroundColor

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
// DecorView.java
private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
// insets null animate false
WindowManager.LayoutParams attrs = getAttributes();
// 获取系统状态栏的显示属性
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
if (!mIsFloating && ActivityManager.isHighEndGfx()) {
boolean disallowAnimate = !isLaidOut();
disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
mLastWindowFlags = attrs.flags;
if (insets != null) {
// 省略相关代码
}
// 这里相当于是横竖屏判断
boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
// 更新Navigation
updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
navBarSize, navBarToRightEdge, 0 /* rightInset */,
animate && !disallowAnimate);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
// 更新Status
updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
mLastTopInset, false /* matchVertical */, statusBarRightInset,
animate && !disallowAnimate);
}
// 省略代码
return insets;
}

3.DecorView.updateColorViewInt

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
// DecorView.java
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
// animate false
state.present = (sysUiVis & state.systemUiHideFlag) == 0
&& (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
&& ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
|| force);
boolean show = state.present
&& (color & Color.BLACK) != 0
&& ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force);
boolean showView = show && !isResizing() && size > 0;
boolean visibilityChanged = false;
View view = state.view;
int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
// 横屏和竖屏展示的为位置不一样,分别对应着设置的horizontalGravity和verticalGravity
int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
if (view == null) {
if (showView) {
// 为空会创建。相当于创建了Navigation 和Status View
// 即改变StatusBar/NavigationBar 实际上是改变了一个View 的颜色
state.view = view = new View(mContext);
view.setBackgroundColor(color);
view.setTransitionName(state.transitionName);
view.setId(state.id);
visibilityChanged = true;
view.setVisibility(INVISIBLE);
state.targetVisibility = VISIBLE;
LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
resolvedGravity);
lp.rightMargin = rightMargin;
addView(view, lp);
updateColorViewTranslations();
}
} else {
int vis = showView ? VISIBLE : INVISIBLE;
visibilityChanged = state.targetVisibility != vis;
state.targetVisibility = vis;
LayoutParams lp = (LayoutParams) view.getLayoutParams();
if (lp.height != resolvedHeight || lp.width != resolvedWidth
|| lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
lp.height = resolvedHeight;
lp.width = resolvedWidth;
lp.gravity = resolvedGravity;
lp.rightMargin = rightMargin;
view.setLayoutParams(lp);
}
if (showView) {
view.setBackgroundColor(color);
}
}
// 省略颜色修改无关代码
state.visible = show;
state.color = color;
}

setStatusBarColor(api 21以上)这个流程可以分析出来,ActivityDecoeViewStatusBarNavigationBar对应的是一个普通的View(系统中的StatusBarNavigationBar都对应着一个WindowStatusBar中展示的电量、wifi等都是Window中的ViewNavigationBar中展示的返回键、Home键等也是Window中的View)。可以通过反射改变StatusBar颜色和大小来验证(看源码也行的)DecoeView对应着的是普通的View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 利用反射来设置StatusBar(DecorView中对应的StatusBar)颜色和高度(需要api21 以上)
*/
public void changeStatusBarColor2(View view) {
// reflect 改变
try {
Field field = getWindow().getDecorView().getClass().getDeclaredField("mStatusColorViewState");
field.setAccessible(true);
Object object = field.get(getWindow().getDecorView());
Field objectField = object.getClass().getDeclaredField("view");
objectField.setAccessible(true);
View view1 = (View) objectField.get(object);
Log.e("ChangeStatusBarColor", "status bar height = " + view1.getHeight());
view1.setBackgroundColor(0xffff0000);
ViewGroup.LayoutParams params = view1.getLayoutParams();
params.height = 200;
view1.setLayoutParams(params);
} catch (Exception e) {
e.printStackTrace();
}
}

WindowManager.addView()流程。

1.执行到WindowManagerImpl.addView()WindowManagerImpl继承WindowManager)。

1
2
3
4
5
6
7
// WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
// 在Activity 中调用的话,这里的mParentWindow 就会是Activity 中的Window。
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

2.执行到WindowManagerGlobal.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
// WindowManagerGlobal.java
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");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
... // 省略很多代码
ViewRootImpl root;
synchronized (mLock) {
... // 省略很多代码
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view); // 在mViews中增加了需要add 的View
mRoots.add(root); // 在mRoots 中增加新的ViewRootImpl
mParams.add(wparams); // mParams 中增加新的WindowManager.LayoutParams
}
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}

一个WindowManager中对应着一系列的mViewsmRootsmParams,也就是WindowManager管理着多个Window,每一个Window都对应着mViews[k]mRoots[k]mParams[k]如下所示。

1
2
3
4
5
// WindowManagerGlobal.java
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();

3.执行到ViewRootImpl.setView()方法。ViewRootImpl相当于ViewViewManager之间的桥梁。里面的代码比较的长,会执行到ViewRootImpl.requestLayout()

4.执行到ViewRootImpl.requestLayout()方法以及mWindowSession.addToDisplay,其中ViewRootImpl.requestLayout()会接着执行到了ViewRootImpl.scheduleTraversals()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 这里通过mChoreographer.postCallback,最后相当于通过Handler发送了一个消息
//最后会执行到mTraversalRunnable的run
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

5.执行到TraversalRunnable.run(),最后执行到了ViewRootImpl.doTraversal()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ViewRootImpl.java
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}

6.执行到ViewRootImpl.performTraversals()。计算希望的大小,
判断是否是第一次,判断视图是否修改(例如:内容区域),判断Window 是否需要调整大小,判断是否改变了View 的可见性。最后会执行到ViewRootImpl.performMeasure()ViewRootImpl.performLayout()ViewRootImpl.performDraw()

7.ViewRootImpl.performMeasure()会调用View.measure对View进行测量;ViewRootImpl.performLayout()会调用View.layout对View进行布局。ViewRootImpl.performDraw()会调用ViewRootImpl.draw,然后会调用到ViewRootImpl.drawSoftware,最后就会调用到View.draw。这一系列的操作完成以后就会相当于展示创建了一个View,大小、位置以及视图也完全处理完毕。

8.第4步中执行到ViewRootImpl.requestLayout()以后的过程已经分析完毕,相当于是对View 进行了测量、布局以及绘制。接下来执行mWindowSession.addToDisplaymWindowSession对应的是一个IWindowSession.aidl,Java 层实现的类是com.android.server.wm.Session

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) {
// mService 对应的是WindowManagerService
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

9.执行WindowManagerService.addWindow。这里就是Window的添加等,没有细看。

总结:通过WindowManagerWindow的操作实际上就是对里面View的操作,展示的也是里面View的信息。

WindowManager.removeView()流程。

这里有一个问题需要考虑一下,通过Activity.getWindowManager().removeView(View view),这里removeView的参数是什么?Activity中设置的View中的一个子View`吗?

addView第2步就有说明,这个View其实是一个整体的View,不是某个View中的子View而是一个Window 中展示的View。

1.直接看到WindowManagerGlobal.removeView()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// WindowManagerGlobal.java
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// 获取到view 对应的index,默认是-1
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);
}
}

2.执行WindowManagerGlobal.removeViewLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// WindowManagerGlobal.java
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
// 执行remove 操作。
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
// 执行die 操作。
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
// 将View 加入到mDyingViews 中。
mDyingViews.add(view);
}
}
}

总结:removeView也是对View的操作。

PopupWindow显示流程

showAsDropDown(View anchor)/showAtLocation(View parent, int gravity, int x, int y)preparePopup(WindowManager.LayoutParams p),最后invokePopup(WindowManager.LayoutParams p),这里不再分析。

Dialog显示流程

show(),这里不再分析。

其它内容

1.查看当前Window的信息

1
2
3
4
5
6
Egos-MacBook-Pro:~ Egos$ adb shell dumpsys window windows | grep -E 'mCurrentFocus'
// Activity 对应的当前Window 信息
mCurrentFocus=Window{1cd90da2 u0 com.egos.samples/com.egos.samples.window.WindowActivity}
Egos-MacBook-Pro:~ Egos$ adb shell dumpsys window windows | grep -E 'mCurrentFocus'
// Activity 打开PopupWindow(使用Dialog 的时候效果也是一样的)之后对应的当前Window 信息
mCurrentFocus=Window{1ad2aefa u0 com.egos.samples/com.egos.samples.window.WindowActivity}

2.adb shell dumpsys window 查看当前所有的Window信息,包括Window的大小、位置。下面一个例子,拿中Window #3 来展示mAttrs就展示了大小,mHasSurface展示了起始位置。WindownManage.LayoutParams.mTitle 对应了名称。

1
2
3
4
5
6
7
8
Window #3 Window{914ba56 u0 com.egos.samples/com.egos.samples.window.WindowActivity}:
mDisplayId=0 stackId=1 mSession=Session{b853937 2930:u0a10102} mClient=android.os.BinderProxy@4d81c71
mOwnerUid=10102 mShowToOwnerOnly=true package=com.egos.samples appop=NONE
mAttrs=WM.LayoutParams{(0,0)(200x200) sim=#20 ty=2 fl=#0}
Requested w=200 h=200 mLayoutSeq=152
mHasSurface=true mShownPosition=[440,824] isReadyForDisplay()=true hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{31dbad7 com.egos.samples/com.egos.samples.window.WindowActivity}:
Surface: shown=true layer=21015 alpha=1.0 rect=(440.0,824.0) 200.0 x 200.0

一个详细的例子。下面总共有9个Window(0,1,..8),可以看到NavigationBarStatusBar(StatusBarWindowManager管理着StatusBar Window的)等其实都是Window。也非常好验证,将StatusBar向下滑动唤出全屏的通知栏,执行adb shell dumpsys window会发现StatusBar 变成了全屏(从mAttrs可以看出)。

PS:PhoneStatusBar.java控制着StatusBarNavigationBar。详情可以查看源码frameworks.base.packages.SystemUI包下面的代码,包含了StatusBarNavigationBar、截屏Window、锁屏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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
Egos-MacBook-Pro:aosp Egos$ adb shell dumpsys window
//.. 这里省略了很多信息,下面总共有9个Window(0,1,..8)
WINDOW MANAGER WINDOWS (dumpsys window windows)
Window #8 Window{c65e61c u0 NavigationBar}:
mDisplayId=0 stackId=0 mSession=Session{39b4687 2046:u0a10023} mClient=android.os.BinderProxy@d23238f
mOwnerUid=10023 mShowToOwnerOnly=false package=com.android.systemui appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillxfill) sim=#20 ty=2019 fl=#1840068 fmt=-3}
Requested w=1080 h=144 mLayoutSeq=271
mHasSurface=true mShownPosition=[0,1776] isReadyForDisplay()=true hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{4b5dd83 NavigationBar}:
mAnimating=false mLocalAnimating=false mAnimationIsEntrance=true mAnimation=null mStackClip=1
Surface: shown=true layer=211000 alpha=1.0 rect=(0.0,1776.0) 1080.0 x 144.0
Window #7 Window{6fa7c9a u0 StatusBar}:
mDisplayId=0 stackId=0 mSession=Session{39b4687 2046:u0a10023} mClient=android.os.BinderProxy@4f62d45
mOwnerUid=10023 mShowToOwnerOnly=false package=com.android.systemui appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillx72) gr=#30 sim=#10 ty=2000 fl=#81840048 fmt=-3 vsysui=0x600}
Requested w=1080 h=72 mLayoutSeq=271
mHasSurface=true mShownPosition=[0,0] isReadyForDisplay()=true hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{83a4c32 StatusBar}:
mAnimating=false mLocalAnimating=false mAnimationIsEntrance=true mAnimation=null mStackClip=1
Surface: shown=true layer=161000 alpha=1.0 rect=(0.0,0.0) 1080.0 x 72.0
Window #6 Window{7cba79f u0 KeyguardScrim}:
mDisplayId=0 stackId=0 mSession=Session{ddf9b54 1962:1000} mClient=android.view.ViewRootImpl$W@2c300ec
mOwnerUid=1000 mShowToOwnerOnly=false package=android appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=2029 fl=#1110900 pfl=0x1 fmt=-3 or=5 vsysui=0x3610000}
Requested w=1080 h=1776 mLayoutSeq=58
mHasSurface=false mShownPosition=[0,0] isReadyForDisplay()=false hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{b8b9c3d KeyguardScrim}:
mLastFreezeDuration=+2m59s819ms
Window #5 Window{401d23f u0 AssistPreviewPanel}:
mDisplayId=0 stackId=0 mSession=Session{39b4687 2046:u0a10023} mClient=android.os.BinderProxy@eb66e5e
mOwnerUid=10023 mShowToOwnerOnly=true package=com.android.systemui appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillx750) gr=#800053 sim=#31 ty=2033 fl=#1000118 fmt=-3 vsysui=0x700}
Requested w=0 h=0 mLayoutSeq=57
mHasSurface=false mShownPosition=[0,0] isReadyForDisplay()=false hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{10d4994 AssistPreviewPanel}:
mShownAlpha=0.0 mAlpha=1.0 mLastAlpha=0.0
Window #4 Window{7af7b99 u0 DockedStackDivider}:
mDisplayId=0 stackId=0 mSession=Session{39b4687 2046:u0a10023} mClient=android.os.BinderProxy@ab9dce0
mOwnerUid=10023 mShowToOwnerOnly=false package=com.android.systemui appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillx144) sim=#20 ty=2034 fl=#21840028 pfl=0x40 fmt=-3 vsysui=0x700}
Requested w=1080 h=144 mLayoutSeq=271
mPolicyVisibility=false mPolicyVisibilityAfterAnim=false mAppOpVisibility=true mAttachedHidden=false
mHasSurface=false mShownPosition=[0,0] isReadyForDisplay()=false hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{d3489e7 DockedStackDivider}:
mShownAlpha=0.0 mAlpha=1.0 mLastAlpha=0.0
Window #3 Window{699ad0c u0 com.egos.samples/com.egos.samples.toast.ToastActivity}:
mDisplayId=0 stackId=1 mSession=Session{153e999 3360:u0a10102} mClient=android.os.BinderProxy@e9ef03f
mOwnerUid=10102 mShowToOwnerOnly=true package=com.egos.samples appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillxfill) sim=#120 ty=1 fl=#81810100 pfl=0x20000 wanim=0x1030465 vsysui=0x600 needsMenuKey=2}
Requested w=1080 h=1704 mLayoutSeq=271
mHasSurface=true mShownPosition=[0,0] isReadyForDisplay()=true hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{7b190d3 com.egos.samples/com.egos.samples.toast.ToastActivity}:
Surface: shown=true layer=21015 alpha=1.0 rect=(0.0,0.0) 1080.0 x 1920.0
Window #2 Window{df42f03 u0 com.android.launcher3/com.android.launcher3.Launcher}:
mDisplayId=0 stackId=0 mSession=Session{da8c367 2497:u0a10014} mClient=android.os.BinderProxy@820f3b2
mOwnerUid=10014 mShowToOwnerOnly=true package=com.android.launcher3 appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillxfill) sim=#120 ty=1 fl=#81910100 fmt=-2 wanim=0x1030465 vsysui=0x700 needsMenuKey=2}
Requested w=1080 h=1920 mLayoutSeq=227
mHasSurface=false mShownPosition=[0,0] isReadyForDisplay()=false hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{371f101 com.android.launcher3/com.android.launcher3.Launcher}:
mLastFreezeDuration=+2m59s941ms
mWallpaperX=0.0 mWallpaperY=0.5
Window #1 Window{2126153 u0 com.android.systemui/com.android.systemui.recents.RecentsActivity}:
mDisplayId=0 stackId=0 mSession=Session{39b4687 2046:u0a10023} mClient=android.os.BinderProxy@c0a3d42
mOwnerUid=10023 mShowToOwnerOnly=true package=com.android.systemui appop=NONE
mAttrs=WM.LayoutParams{(0,0)(fillxfill) sim=#120 ty=1 fl=#81910100 pfl=0x24040 fmt=-3 vsysui=0x700 needsMenuKey=2}
Requested w=1080 h=1920 mLayoutSeq=187
mHasSurface=false mShownPosition=[0,0] isReadyForDisplay()=false hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{9f984a2 com.android.systemui/com.android.systemui.recents.RecentsActivity}:
mAnimating=true mLocalAnimating=false mAnimationIsEntrance=false mAnimation=null mStackClip=1
XForm: has=true hasLocal=false {alpha=1.0 matrix=[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
mShownAlpha=0.0 mAlpha=1.0 mLastAlpha=0.0
mGlobalScale=1.0 mDsDx=1.0 mDtDx=0.0 mDsDy=0.0 mDtDy=1.0
Window #0 Window{c4fa95f u0 com.android.systemui.ImageWallpaper}:
mDisplayId=0 stackId=0 mSession=Session{39b4687 2046:u0a10023} mClient=android.os.BinderProxy@6adfe
mOwnerUid=10023 mShowToOwnerOnly=true package=com.android.systemui appop=NONE
mAttrs=WM.LayoutParams{(0,0)(1920x1920) gr=#800033 ty=2013 fl=#318 fmt=1 wanim=0x10302f2}
Requested w=1920 h=1920 mLayoutSeq=238
mIsImWindow=false mIsWallpaper=true mIsFloatingLayer=true mWallpaperVisible=false
mHasSurface=true mShownPosition=[0,0] isReadyForDisplay()=false hasSavedSurface()=false mWindowRemovalAllowed=false
WindowStateAnimator{860a6e8 com.android.systemui.ImageWallpaper}:
Surface: shown=false layer=21000 alpha=1.0 rect=(0.0,0.0) 1920.0 x 1920.0
mLastFreezeDuration=+2m58s925ms
mWallpaperX=0.0 mWallpaperY=0.5
mCurConfiguration={1.0 310mcc260mnc [en_US] ldltr sw360dp w360dp h568dp 480dpi nrml port finger qwerty/v/v dpad/v s.6}
mHasPermanentDpad=false
mCurrentFocus=Window{699ad0c u0 com.egos.samples/com.egos.samples.toast.ToastActivity}
mFocusedApp=AppWindowToken{26954dc token=Token{2ae93ae ActivityRecord{9a6a429 u0 com.egos.samples/.toast.ToastActivity t169}}}
mInTouchMode=true mLayoutSeq=271
mLastDisplayFreezeDuration=0 due to Window{df42f03 u0 com.android.launcher3/com.android.launcher3.Launcher}
mLastWakeLockHoldingWindow=null mLastWakeLockObscuringWindow=Window{699ad0c u0 com.egos.samples/com.egos.samples.toast.ToastActivity}
Egos-MacBook-Pro:aosp Egos$

3.PhoneWindowManager.java拦截键盘消息(比如home操作、截屏操作等)的处理类。

4.通过Android Studio查看View Hierarchy等信息。分析布局更加全面。
技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
View Hierarchy:
com.android.internal.policy.DecorView{3afab2c V.E..... ... 0,0-1080,1920}
android.widget.LinearLayout{7fb79f5 V.E..... ... 0,0-1080,1776}
android.view.ViewStub{db9c38a G.E..... ... 0,0-0,0 #10203e8 android:id/action_mode_bar_stub}
android.widget.FrameLayout{8687dfb V.E..... ... 0,0-1080,1776}
android.support.v7.widget.ActionBarOverlayLayout{6024e18 V.E..... ... 0,0-1080,1776 #7f0c0055 app:id/decor_content_parent}
android.support.v7.widget.ContentFrameLayout{156c071 V.E..... ... 0,0-1080,1776 #1020002 android:id/content}
android.widget.LinearLayout{8f88e56 V.E..... ... 0,0-1080,1776}
android.support.v7.widget.AppCompatButton{9b6fed7 VFED..C. ... 0,240-635,384}
android.support.v7.widget.AppCompatButton{5da33c4 VFED..C. ... 0,384-659,528}
android.support.v7.widget.AppCompatButton{745f2ad VFED..C. ... 0,528-653,672}
android.support.v7.widget.ActionBarContainer{78739e2 V.ED.... ... 0,72-1080,240 #7f0c0056 app:id/action_bar_container}
android.support.v7.widget.Toolbar{f02b173 V.E..... ... 0,0-1080,168 #7f0c0057 app:id/action_bar}
android.support.v7.widget.AppCompatTextView{828c830 V.ED.... ... 48,43-280,124}
android.support.v7.widget.ActionMenuView{b7c0ca9 V.E..... ... 1080,0-1080,168}
android.support.v7.widget.ActionBarContextView{363122e G.E..... ... 0,0-0,0 #7f0c0058 app:id/action_context_bar}
android.view.View{e4cf1cf V.ED.... ... 0,1776-1080,1920 #1020030 android:id/navigationBarBackground}
android.view.View{d8a375c V.ED.... ... 0,0-1080,72 #102002f android:id/statusBarBackground}