理解WindowManager
WindowManager类顾名思义就是用来管理Window类。Window在另外一篇有探究理解Window。
1、WindowManager
WindowManager也是接口,继承自ViewManager接口,它的实现类是WindowManagerImpl。都在android.view包中。
1.1、ViewManager
ViewManager接口中只定义了三个接口方法,用来添加、删除、更新View。
/** Interface to let you add and remove child views to an Activity. To get an instance
*/
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为什么又要继承ViewManager多此一举呢?ViewManager是更为通用的抽象,而WindowManager则在此基础之上添加定义了一些方法,还有很多常量。
1.2、WindowManagerImpl
WindowManager接口的实现类是WindowManagerImpl。从理解Window一文中得知获取到的WindowManager其实就是WindowManagerImpl类型实例(以参数wm不为空为例):
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}2、WindowManager.LayoutParams
WindowManager用来管理Window,该类定义了一个静态内部类WindowManager.LayoutParams,其中定义了很多和Window相关的Flag,用来控制Window的显示。
2.1、窗口类型Type
理解Window文中提到有三种类型的Window,它们对应的Type类型都有各自的范围
①Application windows(1~99)
/**
* 代表普通应用程序窗口的窗口类型的开始。
*/
public static final int FIRST_APPLICATION_WINDOW = 1;
/**
* Window type: an application window that serves as the "base" window
* of the overall application; all other application windows will
* appear on top of it.
* In multiuser systems shows only on the owning user's window.
*/
public static final int TYPE_BASE_APPLICATION = 1;
/**
* Window type: a normal application window. The {@link #token} must be
* an Activity token identifying who the window belongs to.
* In multiuser systems shows only on the owning user's window.
*/
public static final int TYPE_APPLICATION = 2;
/**
* Window type: special application window that is displayed while the
* application is starting. Not for use by applications themselves;
* this is used by the system to display something until the
* application can show its own windows.
* In multiuser systems shows on all users' windows.
*/
public static final int TYPE_APPLICATION_STARTING = 3;
/**
* Window type: a variation on TYPE_APPLICATION that ensures the window
* manager will wait for this window to be drawn before the app is shown.
* In multiuser systems shows only on the owning user's window.
*/
public static final int TYPE_DRAWN_APPLICATION = 4;
/**
* 应用程序窗口类型的结尾。
*/
public static final int LAST_APPLICATION_WINDOW = 99;②Sub-windows(1000~1999)
/**
* Start of types of sub-windows. The {@link #token} of these windows
* must be set to the window they are attached to. These types of
* windows are kept next to their attached window in Z-order, and their
* coordinate space is relative to their attached window.
*/
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
/**
* End of types of sub-windows.
*/
public static final int LAST_SUB_WINDOW = 1999;③System windows(2000~2999)
/**
* Start of system-specific window types. These are not normally
* created by applications.
*/
public static final int FIRST_SYSTEM_WINDOW = 2000;
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
...
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
/**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;此外Window类型还有一个隐藏的INVALID_WINDOW_TYPE,系统内部使用。
/**
* Used internally when there is no suitable type available.
*/
public static final int INVALID_WINDOW_TYPE = -1;2.2、Window相关Flag
WindowManager.LayoutParams类中目前还定义了25种控制Window显示的Flag:
/**
* 各种行为Flags,默认为无。
*
* @see #FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
* @see #FLAG_DIM_BEHIND
* @see #FLAG_NOT_FOCUSABLE
* @see #FLAG_NOT_TOUCHABLE
* @see #FLAG_NOT_TOUCH_MODAL
* @see #FLAG_TOUCHABLE_WHEN_WAKING
* @see #FLAG_KEEP_SCREEN_ON
* @see #FLAG_LAYOUT_IN_SCREEN
* @see #FLAG_LAYOUT_NO_LIMITS
* @see #FLAG_FULLSCREEN
* @see #FLAG_FORCE_NOT_FULLSCREEN
* @see #FLAG_SECURE
* @see #FLAG_SCALED
* @see #FLAG_IGNORE_CHEEK_PRESSES
* @see #FLAG_LAYOUT_INSET_DECOR
* @see #FLAG_ALT_FOCUSABLE_IM
* @see #FLAG_WATCH_OUTSIDE_TOUCH
* @see #FLAG_SHOW_WHEN_LOCKED
* @see #FLAG_SHOW_WALLPAPER
* @see #FLAG_TURN_SCREEN_ON
* @see #FLAG_DISMISS_KEYGUARD
* @see #FLAG_SPLIT_TOUCH
* @see #FLAG_HARDWARE_ACCELERATED
* @see #FLAG_LOCAL_FOCUS_MODE
* @see #FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
*/对源码比较了解的会发现,Android系统相关的类中经常会出现一大堆定义常量的参数,各种Flag或者Type。这些完全不用记住,用的时候会查看API,即拿即用(所以一定要学会看源码,看API;比起背书,更重要的是如何“渔”)。举几个其中比较熟悉的Flag:
①FLAG_KEEP_SCREEN_ON
/** 只要该窗口对用户可见,请保持设备的屏幕处于打开状态并保持明亮*/
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;②FLAG_LAYOUT_NO_LIMITS
/** 窗口标志:允许窗口扩展到屏幕之外 */
public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;③FLAG_FULLSCREEN:设置Activity全屏,但是对于异形屏、水滴屏等需要额外设置,参考《Activity页面全屏探究》。
/**
* Window flag: hide all screen decorations (such as the status bar) while
* this window is displayed. This allows the window to use the entire
* display space for itself -- the status bar will be hidden when
* an app window with this flag set is on the top layer. A fullscreen window
* will ignore a value of {@link #SOFT_INPUT_ADJUST_RESIZE} for the window's
* {@link #softInputMode} field; the window will stay fullscreen
* and will not resize.
*
* <p>This flag can be controlled in your theme through the
* {@link android.R.attr#windowFullscreen} attribute; this attribute
* is automatically set for you in the standard fullscreen themes
* such as {@link android.R.style#Theme_NoTitleBar_Fullscreen},
* {@link android.R.style#Theme_Black_NoTitleBar_Fullscreen},
* {@link android.R.style#Theme_Light_NoTitleBar_Fullscreen},
* {@link android.R.style#Theme_Holo_NoActionBar_Fullscreen},
* {@link android.R.style#Theme_Holo_Light_NoActionBar_Fullscreen},
* {@link android.R.style#Theme_DeviceDefault_NoActionBar_Fullscreen}, and
* {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_Fullscreen}.</p>
*/
public static final int FLAG_FULLSCREEN = 0x00000400;3、布局是如何被添加到Window中的?
在DecorView添加到Window中的流程一文中有详细的源码分析。
4、Window操作流程
前面讲到,WindowManager继承自ViewManager接口,ViewManager中定义了三种接口方法。这几个方法基本流程相似,来看看其中一个updateViewLayout()方法的实现流程。
4.1、updateViewLayout()
直接从WindowManager的实现类WindowManagerImpl看:
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}通过WindowManagerGlobal类型的变量mGlobal的同名方法
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) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}调用ViewRootImpl的setLayoutParams()方法
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
final int oldInsetLeft = mWindowAttributes.surfaceInsets.left;
final int oldInsetTop = mWindowAttributes.surfaceInsets.top;
final int oldInsetRight = mWindowAttributes.surfaceInsets.right;
final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom;
final int oldSoftInputMode = mWindowAttributes.softInputMode;
final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets;
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
&& (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) {
Slog.d(mTag, "setLayoutParams: FLAG_KEEP_SCREEN_ON from true to false!");
}
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
// Preserve compatible window flag if exists.
final int compatibleWindowFlag = mWindowAttributes.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
// Transfer over system UI visibility values as they carry current state.
attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs);
if ((mWindowAttributesChangesFlag
& WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
// Recompute system ui visibility.
mAttachInfo.mRecomputeGlobalAttributes = true;
}
if ((mWindowAttributesChangesFlag
& WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) {
// Request to update light center.
mAttachInfo.mNeedsUpdateLightCenter = true;
}
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
mWindowAttributes.surfaceInsets.set(
oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
} else if (mWindowAttributes.surfaceInsets.left != oldInsetLeft
|| mWindowAttributes.surfaceInsets.top != oldInsetTop
|| mWindowAttributes.surfaceInsets.right != oldInsetRight
|| mWindowAttributes.surfaceInsets.bottom != oldInsetBottom) {
mNeedsRendererSetup = true;
}
applyKeepScreenOnFlag(mWindowAttributes);
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
}
// Don't lose the mode we last auto-computed.
if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
== WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
& ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
| (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
}
mWindowAttributesChanged = true;
scheduleTraversals();
}
}方法虽然很长,但是只需要看最后的scheduleTraversals()方法进入下一步,该方法在ViewRootImpl中被调用的次数非常多。
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}其中调用了mChoreographer的postCallback()方法,mChoreographer就是View中大名鼎鼎的Choreographer类,俗称“舞蹈指导”。
/**
* Posts a callback to run on the next frame.
* <p>
* The callback runs once then is automatically removed.
* </p>
*
* @param callbackType The callback type.
* @param action The callback action to run during the next frame.
* @param token The callback token, or null if none.
*
* @see #removeCallbacks
* @hide
*/
@TestApi
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}接着调用postCallbackDelayed()方法
/**
* Posts a callback to run on the next frame after the specified delay.
* <p>
* The callback runs once then is automatically removed.
* </p>
*
* @param callbackType The callback type.
* @param action The callback action to run during the next frame after the specified delay.
* @param token The callback token, or null if none.
* @param delayMillis The delay time in milliseconds.
*
* @see #removeCallback
* @hide
*/
@TestApi
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}通过Handler回到主线程更新UI
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}再回过头来看看当时执行的Runnable对象定义如下
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();TraversalRunnable类也很简单,run()执行doTraversal()方法
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}在ViewRootImpl中doTraversal()定义如下
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}调用performTraversals()方法,源码太长久不贴了。内部会调用relayoutWindow通过IWindowSession更新视图。该方法还会调用performMeasuer()、performLayout()、performDraw()方法,分别执行View的measure、layout和draw,最终完成View的绘制。

