首语
WMS 是Android中重要的服务,它是WindowManager的管理者。
Window是一个抽象类,它的具体实现为PhoneWindow,对View进行管理。
WindowManager 是一个接口类,继承自接口ViewManager,用来管理Window的,它的实现类为WindowManagerImpl,如果想要对Window进行添加、更新和删除操作就可以使用WindowManager,WindowManager会将具体的工作交给WMS进行处理,WindowManager和WMS之间通过Binder来进行跨进程通信。
WMS的主要职责有
- 窗口管理
WMS是窗口的管理者,它负责窗口的启动、添加、删除,此外窗口的大小和层级也是WMS进行管理。窗口管理的核心成员有DisplayContent、WindowToken和WindowState.
- 窗口动画
窗口间进行切换时,使用窗口动画显得更流畅,窗口动画由WMS的动画子系统负责,动画子系统的管理者为WindowAnimator。
- 输入系统的中转
通过对窗口的触摸产生触摸事件,InputManagerService会对触摸事件进行处理,他会寻找合适的窗口来处理触摸反馈信息。
- Surface 管理
窗口不具备绘制功能,因此每一个窗口需要有一块Surface来供自己绘制,为每个窗口分配Surface是由WMS来完成的。
启动
服务启动就不详细展开了,在分析系统启动 SystemServer进程已经了解了,重点关注启动WMS流程。首先调用WMS的main
方法创建WMS实例,然后调用WMS的onInitReady
方法
public final class SystemServer implements Dumpable {WindowManagerService wm = null;private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...t.traceBegin("StartInputManagerService");inputManager = new InputManagerService(context);t.traceEnd();t.traceBegin("StartWindowManagerService");// WMS needs sensor service readymSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);wm = WindowManagerService.main(context, inputManager, !mFirstBoot,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);ServiceManager.addService(Context.INPUT_SERVICE, inputManager,/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);t.traceEnd();t.traceBegin("SetWindowManagerService");//WMS传递给AMSmActivityManagerService.setWindowManager(wm);t.traceEnd();t.traceBegin("WindowManagerServiceOnInitReady");//初始化完成wm.onInitReady();t.traceEnd();...}
}
首先调用DisplayThread的getHandler
方法获取Handler实例,DisplayThread是系统共享的单例前台线程。这是一个用于影响显示内容的操作的线程,需要具有最小的延迟。这个线程应该基本上只被WindowManager、DisplayManager和InputManager用来执行实时快速操作。线程名为"android.display",run
方法内部创建了WMS,构造方法中进行了大量初始化工作。
onInitReady
方法中首先通过initPolicy
方法初始化了WindowManagerPolicy,它用来的定义一个窗口策略所要遵循的规范,接着通过Watchdog监控WMS的运行情况,如果被监控的服务出现了死锁,则会杀死WatchDog所在的SystemServer进程。
这里涉及线程的变化和优先级,简单总结下,首先system_server的线程会创建WMS,WMS在"android:display"线程中实现,创建WMS的优先级更高,因此system_server的线程需要等待WMS创建完成后才会被唤醒继续执行下面的代码。而PWM的init
方法在"android:ui"线程执行,优先级又高于"android:display",因此,“android:display"线程需要等待PWM的init
方法执行完成后才会被唤醒执行下面的代码。优先级顺序由高到低为"android:ui”->“android:display”->“system_server”。
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {@VisibleForTesting//接口类,具体实现类为PhoneWindowManager,WMS调用main方法创建实现类,它提供了WindowManager的所有UI特定行为,WindowManager启动时会创建一个实例,并允许自定义窗口分层、特殊窗口类型、键分发和布局, 因为它提供了与WindowManager的深度交互,所以可以通过各种上下文调用此接口上的特定方法,并对它们可以执行的操作施加各种限制。WindowManagerPolicy mPolicy;//存储Session,它用于进程间通信,其它应用想和WMS进程通信需要经过Session,每个应用进程对应一个Session,WMS会保存这些Session来记录所有向WMS提出窗口管理服务的客户端。final ArraySet<Session> mSessions = new ArraySet<>();//用来存储正在调整大小的窗口列表,final ArrayList<WindowState> mResizingWindows = new ArrayList<>();//用来管理窗口的动画以及特效动画final WindowAnimator mAnimator;//输入系统的管理者,IMS会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口管理者,因此WMS作为IMS的中转站final InputManagerService mInputManager;public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, WindowManagerPolicy policy,ActivityTaskManagerService atm) {final WindowManagerService wms = main(context, im, showBootMsgs, policy, atm,new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new,SurfaceControl.Builder::new);WindowManagerGlobal.setWindowManagerServiceForSystemProcess(wms);return wms;}@VisibleForTestingpublic static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,DisplayWindowSettingsProvider displayWindowSettingsProvider,Supplier<SurfaceControl.Transaction> transactionFactory,Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {final WindowManagerService[] wms = new WindowManagerService[1];DisplayThread.getHandler().runWithScissors(() ->wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm,displayWindowSettingsProvider, transactionFactory,surfaceControlFactory), 0);return wms[0];}private WindowManagerService(Context context, InputManagerService inputManager,boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,DisplayWindowSettingsProvider displayWindowSettingsProvider,Supplier<SurfaceControl.Transaction> transactionFactory,Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {//持有ActivityManagerService 引用mActivityManager = ActivityManager.getService();//创建WindowAnimator 管理所有窗口动画mAnimator = new WindowAnimator(this);mRoot = new RootWindowContainer(this);}public void onInitReady() {initPolicy();// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);createWatermark();showEmulatorDisplayOverlayIfNeeded();}private void initPolicy() {UiThread.getHandler().runWithScissors(new Runnable() {@Overridepublic void run() {WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());mPolicy.init(mContext, WindowManagerService.this);}}, 0);}
}
//frameworks/base/services/core/java/com/android/server/DisplayThread.java
public final class DisplayThread extends ServiceThread {private DisplayThread() {// DisplayThread runs important stuff, but these are not as important as things running in// AnimationThread. Thus, set the priority to one lower.super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);}public static Handler getHandler() {synchronized (DisplayThread.class) {ensureThreadLocked();return sHandler;}}
}
WindowManager
关联类
ViewManager中定义了三个方法,分别用来添加、更新和删除View。
//frameworks/base/core/java/android/view/ViewManager.java
public interface ViewManager
{public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view);
}
这些方法的参数都是View类型,说明Window是以View的形式存在的。WindowManager在继承ViewManager的同时,又加入很多功能,诸如Window的类型和层级相关的常量、内部类及一些方法,例如removeViewImmediate
规定在这个方法返回前立即执行View.onDetachedFromWindow
,来完成传入View相关的销毁工作。
Window是一个抽象类,它的具体实现为PhoneWindow,PhoneWindow是何时创建的呢?在Activity启动过程中我们知道会调用ActivityThread的performLaunchActivity
方法,其中又会调用Activity的attach
方法,PhoneWindow就是在Activity的attach
方法中创建的。创建PhoneWindow后,调用PhoneWindow的setWindowManager
方法。
//frameworks/base/core/java/android/app/Activity.java
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.setWindowControllerCallback(mWindowControllerCallback);mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);mWindow.getLayoutInflater().setPrivateFactory(this);if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}if (info.uiOptions != 0) {mWindow.setUiOptions(info.uiOptions);}...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();mCurrentConfig = config;mWindow.setColorMode(info.colorMode);mWindow.setPreferMinimalPostProcessing((info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);...
}
setWindowManager
方法在Window中实现,用于设置WindowManager。如果wm为null,则会获取服务名为“window”的系统服务。
//frameworks/base/core/java/android/view/Window.java
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);}mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}
getSystemService
方法调用SystemServiceRegistry类的getSystemService
方法。
//frameworks/base/core/java/android/app/ContextImpl.java
@Overridepublic Object getSystemService(String name) {return SystemServiceRegistry.getSystemService(this, name);}
通过SYSTEM_SERVICE_FETCHERS保存服务,在SystemServiceRegistry的静态代码块中会调用多个registerService
方法,前面提到的window service也是这样保存,创建服务对应的实例是WindowManagerImpl实例,再根据前面的代码,将wm转换为WindowManagerImpl并调用createLocalWindowManager
方法。
//frameworks/base/core/java/android/app/SystemServiceRegistry.java
static {...registerService(Context.WINDOW_SERVICE, WindowManager.class,new CachedServiceFetcher<WindowManager>() {@Overridepublic WindowManager createService(ContextImpl ctx) {return new WindowManagerImpl(ctx);}});
}
public static Object getSystemService(ContextImpl ctx, String name) {if (name == null) {return null;}final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);if (fetcher == null) {if (sEnableServiceNotFoundWtf) {Slog.wtf(TAG, "Unknown manager requested: " + name);}return null;}final Object ret = fetcher.getService(ctx);if (sEnableServiceNotFoundWtf && ret == null) {// Some services do return null in certain situations, so don't do WTF for them.switch (name) {case Context.CONTENT_CAPTURE_MANAGER_SERVICE:case Context.APP_PREDICTION_SERVICE:case Context.INCREMENTAL_SERVICE:case Context.ETHERNET_SERVICE:case Context.CONTEXTHUB_SERVICE:case Context.VIRTUALIZATION_SERVICE:case Context.VIRTUAL_DEVICE_SERVICE:return null;}Slog.wtf(TAG, "Manager wrapper not available: " + name);return null;}return ret;}
private static <T> void registerService(@NonNull String serviceName,@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());}
创建WindowManagerImpl时将创建它的Window作为参数传了进来,这样持有了Window的引用,可以对Window进行操作,比如在Window中添加View,会调用WindowManagerImpl的addView
方法,如下所示:
在addView
方法中,调用WindowManagerGlobal类的addView
方法,这里使用桥接模式,功能实现委托给了WindowManagerGlobal,WindowManagerGlobal在WindowManagerImpl是一个单例。同时可以看出构造函数存在parentWindow,说明WindowManagerImpl会作为那个Window的子Window,一个进程中会有多个WindowManagerImpl实例。
//frameworks/base/core/java/android/view/WindowManagerImpl.java
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private WindowManagerImpl(Context context, Window parentWindow,@Nullable IBinder windowContextToken) {mContext = context;mParentWindow = parentWindow;mWindowContextToken = windowContextToken;mWindowMetricsController = new WindowMetricsController(mContext);}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);}@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyTokens(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());}
Window属性
Window 的属性有很多种,其它有密切的有3种,分别是Type(Window的类型),Flag(Window的标志)和SoftInputMode(软键盘相关模式)
Window的类型和显示次序
Window的类型有很多种,如应用程序窗口、系统窗口、PopWindow、Toast和Dialog等。一般将Window分为三大类型,分别是Application Window(应用程序窗口)、Sub Window(子窗口)、System Window(系统窗口),每个大的类型中又分了很多种类型,它们都定义在WindowManager的静态内部类LayoutParams中。
应用程序窗口
Activity就是一个典型的应用程序窗口,包含的类型如下所示,应用程序窗口的Type值范围为1~99,数值大小涉及窗口的层级。
//frameworks/base/core/java/android/view/WindowManager.java
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//窗口类型开始,代表普通应用程序窗口。public static final int FIRST_APPLICATION_WINDOW = 1;//窗口类型:作为整个应用程序“基础”窗口的应用程序窗口;所有其他应用程序窗口都将显示在其上方。在多用户系统中,仅显示在拥有用户窗口上。public static final int TYPE_BASE_APPLICATION = 1;//窗口类型:一个普通的应用程序窗口。令牌必须是标识窗口属于哪个活动的Activity令牌。在多用户系统中,仅显示在拥有用户窗口上。public static final int TYPE_APPLICATION = 2;//窗口类型:特殊的应用程序窗口,在应用程序启动时显示。不供应用程序本身使用;系统使用它来显示内容,直到应用程序可以显示自己的窗口。在多用户系统中,显示在所有用户的窗口上。public static final int TYPE_APPLICATION_STARTING = 3;//窗口类型:TYPE_APPLICATION的一个变体,确保窗口管理器在应用程序显示之前等待此窗口被绘制。在多用户系统中,仅显示在拥有用户窗口上。public static final int TYPE_DRAWN_APPLICATION = 4;//应用程序窗口类型结束。public static final int LAST_APPLICATION_WINDOW = 99;}
}
子窗口
子窗口不能独立存在,需要依附其它窗口才可以,PopWindow属于子窗口,子窗口的Type值范围为1000~1999,子窗口的类型如下所示:
//frameworks/base/core/java/android/view/WindowManager.java
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//子窗口类型开始。这些窗口的令牌必须设置到它们所附加的窗口上。这些类型的窗口在Z顺序中紧随其附加窗口,并且它们的坐标空间相对于其附加窗口。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;//窗口类型:应用程序窗口顶部的子面板。这些窗口显示在它们附加窗口的顶部和任何TYPE_APPLICATION_PANEL面板之上。public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;//窗口类型:类似于TYPE_APPLICATION_PANEL,但窗口的布局是作为顶级窗口,而不是其容器的子窗口。public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;//窗口类型:用于在媒体窗口顶部显示覆盖的窗口。这些窗口显示在TYPE_APPLICATION_MEDIA和应用程序窗口之间。它们应该是半透明的才有用。这是一个很大的丑陋的方案,所以:隐藏@UnsupportedAppUsagepublic static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;//窗口类型:在应用程序窗口及其子面板窗口顶部的上方子面板。这些窗口显示在它们附加窗口和任何TYPE_APPLICATION_SUB_PANEL面板的顶部。public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;//子窗口类型结束。public static final int LAST_SUB_WINDOW = 1999;}
}
系统窗口
Toast、输入法窗口、系统音量条窗口、系统错误窗口等都属于系统窗口,系统窗口的类型有将近41个,这里只列出了一小部分,系统窗口的取值范围为2000~2999系统窗口的定义如下:
//frameworks/base/core/java/android/view/WindowManager.java
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//系统特定窗口类型开始。这些通常不是由应用程序创建的。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;//窗口类型:电话。这些是非应用程序窗口,提供用户与电话的交互(特别是来电)。这些窗口通常位于所有应用程序之上,但在状态栏后面。在多用户系统中,显示在所有用户的窗口上。已弃用 对于非系统应用程序。请使用TYPE_APPLICATION_OVERLAY代替。@Deprecatedpublic static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//窗口类型:系统窗口,如低电量警告。这些窗口始终位于应用程序窗口之上。在多用户系统中,仅显示在拥有用户的窗口上。已弃用 对于非系统应用程序。请使用TYPE_APPLICATION_OVERLAY代替。@Deprecatedpublic static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//窗口类型:键盘锁窗口。在多用户系统中,显示在所有用户的窗口上。public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//窗口类型:临时通知。在多用户系统中,仅显示在拥有用户的窗口上。已弃用 对于非系统应用程序。请使用TYPE_APPLICATION_OVERLAY代替。@Deprecatedpublic static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//窗口类型:系统覆盖窗口,需要显示在所有其他内容之上。这些窗口不得获取输入焦点,否则会干扰键盘锁。在多用户系统中,仅显示在拥有用户的窗口上。已弃用 对于非系统应用程序。请使用TYPE_APPLICATION_OVERLAY代替。@Deprecatedpublic static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;//窗口类型:从状态栏滑出的面板。在多用户系统中,显示在所有用户的窗口上。public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;//窗口类型:内部系统错误窗口,出现在所有内容之上。在多用户系统中,仅显示在拥有用户的窗口上。已弃用 对于非系统应用程序。请使用TYPE_APPLICATION_OVERLAY代替。@Deprecatedpublic static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;//窗口类型:内部输入法窗口,出现在正常UI之上。当显示此窗口时,应用程序窗口可能会被调整大小或移动,以保持输入焦点可见。在多用户系统中,仅显示在拥有用户的窗口上。public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;//窗口类型:壁纸窗口,放置在任何想要位于壁纸顶部的窗口后面。在多用户系统中,仅显示在拥有用户的窗口上。public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;//窗口类型:(鼠标)指针。在多用户系统中,显示在所有用户的窗口上。public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;//窗口类型:导航栏(当与状态栏不同时)。在多用户系统中,显示在所有用户的窗口上。public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;//窗口类型:应用程序覆盖窗口显示在所有活动窗口(类型介于FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW之间)之上,但位于状态栏或IME等关键系统窗口之下。系统可能会随时更改这些窗口的位置、大小或可见性,以减少用户的视觉混乱并管理资源。需要android.Manifest.permission.SYSTEM_ALERT_WINDOW权限。系统将调整具有此窗口类型的进程的重要性,以减少低内存杀手杀死它们的机会。在多用户系统中,仅显示在拥有用户的屏幕上。public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;//系统窗口类型结束。public static final int LAST_SYSTEM_WINDOW = 2999;//当没有合适的类型可用时,内部使用。隐藏public static final int INVALID_WINDOW_TYPE = -1;}
}
显示次序
当一个进程向WMS申请一个窗口时,WMS会为窗口确定显示次序,为了方便窗口的显示次序管理,手机屏幕可以虚拟的用X、Y、Z轴来表示,其中Z轴垂直于屏幕,从屏幕内指向屏幕外,这样确定窗口显示次序也就是确定窗口在Z轴上的次序,这个次序被称为Z-Order,Type值是Z-Order排序的根据,一般情况下,Type值越大则Z-Order排序越靠前,越靠近用户。
实际上,窗口显示次序由WindowState的两个字段描述,主序mBaseLayer和子序mSubLayer。
- mBaseLayer用于描述窗口及其子窗口在所有窗口的显示位置,主序越大,则窗口及其子窗口的显示位置相对于其它窗口更靠前。
- mSubLayer用于描述子窗口和父窗口之间的相对位置,子序越大,则其它相对子窗口越靠前,反之则越靠后,如果为负数,则处在父窗口的后面。
Window的标志
Window的标志就是Flag,用于控制Window的显示,同样也被定义在WindowManager的内部类LayoutParams中,其中分了很多类,如Flags、SystemFlags、PrivateFlags、SoftInputModeFlags、DisplayFlags、SystemUiVisibilityFlags,一共有将近一百种。
//frameworks/base/core/java/android/view/WindowManager.java
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//窗口标志:只要此窗口对用户可见,即使在屏幕开启时也允许激活锁屏。这可以单独使用,或者与FLAG_KEEP_SCREEN_ON和/或FLAG_SHOW_WHEN_LOCKED结合使用。public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;//窗口标志:此窗口永远不会获得键盘输入焦点,因此用户不能向其发送键盘或其他按钮事件。这些事件将传递给位于其后面的任何可聚焦窗口。设置此标志还会启用FLAG_NOT_TOUCH_MODAL,无论是否明确设置了该标志。设置此标志还意味着窗口不需要与软输入法交互,因此它将独立于任何活动的输入方法进行Z排序和定位(通常这意味着它会在输入方法之上进行Z排序,因此它可以使用整个屏幕显示内容,并在需要时覆盖输入方法。你可以使用FLAG_ALT_FOCUSABLE_IM来修改这种行为。public static final int FLAG_NOT_FOCUSABLE = 0x00000008;//窗口标志:此窗口永远不能接收触摸事件。此标志的目的是让触摸事件由位于此窗口下方(按Z顺序)的某个窗口处理。public static final int FLAG_NOT_TOUCHABLE = 0x00000010;//标志,用于指示当此窗口可见时,应由应用程序进程添加的任何类型为TYPE_TOAST的窗口或需要android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW权限的窗口应被隐藏。@SystemApi@RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 1 << 19;//当设置此标志时,即使为另一个可见窗口设置了SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,TYPE_APPLICATION_OVERLAY类型的窗口仍然保持可见。@RequiresPermission(permission.SYSTEM_APPLICATION_OVERLAY)public static final int PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY = 1 << 3;//指示此窗口是否希望禁用HDR转换。public static final int DISPLAY_FLAG_DISABLE_HDR_CONVERSION = 1 << 0;}
}
//frameworks/base/core/java/android/view/View.java
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {//setSystemUiVisibility(int)的特殊常量:视图已请求系统UI(状态栏)可见(默认状态)。已弃用 SystemUiVisibility标志已弃用。请使用WindowInsetsController代替。@Deprecatedpublic static final int SYSTEM_UI_FLAG_VISIBLE = 0;//此标志只能在subtreeSystemUiVisibility中使用。它被从公共字段中屏蔽,以防止未定义的位干扰开发者。标志,用于隐藏仅主页按钮。除非你是系统UI的特殊部分(例如,设置向导、键盘保护),否则不要使用此标志。@UnsupportedAppUsagepublic static final int STATUS_BAR_DISABLE_HOME = 0x00200000;
}
设置Window的Flag有三种方法,第一种是通过addFlags
,第二种是通过setFlags
,其中addFlags
内部实现还是调用的setFlags
,第三种则是给LayoutParam设置Flag,并通过WindowManager的addView
方法添加。
window.addFlags(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG)window.setFlags(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG)val layoutParams = WindowManager.LayoutParams()layoutParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ONval windowManager = getSystemService(WINDOW_SERVICE) as WindowManagerwindowManager.addView(TextView(this), layoutParams)
软键盘相关模式
软键盘相关模式大约有11种,它和AndroidManifest中Activity的属性android:windowSoftInputMode
是对应的,也可以通过代码去设置SoftInputMode。
//frameworks/base/core/java/android/view/WindowManager.java
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//软输入模式的掩码,用于确定此窗口的软输入区域所需的可视状态。public static final int SOFT_INPUT_MASK_STATE = 0x0f;//软输入模式的可视状态:未指定任何状态。当窗口获得焦点时,系统可能会显示或隐藏软件键盘,以提供更好的用户体验。public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;//软输入模式的可视状态:请不要更改软输入区域的状态。public static final int SOFT_INPUT_STATE_UNCHANGED = 1;//软输入模式的可视状态:请在通常合适的情况下隐藏任何软输入区域(当用户向前导航到您的窗口时)。public static final int SOFT_INPUT_STATE_HIDDEN = 2;//软输入模式的可视状态:当此窗口接收焦点时,请始终隐藏任何软输入区域。public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;//软输入模式的可视状态:请在通常合适的情况下显示软输入区域(当用户向前导航到您的窗口时)。针对android.os.Build.VERSION_CODES.P及更高版本的应用程序,除非在窗口获得焦点时有焦点的视图从View.onCheckIsTextEditor()返回true,否则此标志将被忽略。public static final int SOFT_INPUT_STATE_VISIBLE = 4;//软输入模式的可视状态:当此窗口接收输入焦点时,请始终使软输入区域可见。针对android.os.Build.VERSION_CODES.P及更高版本的应用程序,除非在窗口获得焦点时有焦点的视图从View.onCheckIsTextEditor()返回true,否则此标志将被忽略。public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;//软输入模式的掩码,用于确定窗口应如何调整以适应软输入窗口。public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;//软输入模式的调整选项:未指定任何内容。系统将根据窗口的内容尝试选择一个调整方式。public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;//软输入模式的调整选项:设置为允许窗口在显示输入法时调整大小,使其内容不被输入法覆盖。这不能与SOFT_INPUT_ADJUST_PAN结合使用;如果这两个都没有设置,那么系统将尝试根据窗口的内容选择一个。如果窗口的布局参数标志包括FLAG_FULLSCREEN,则软输入模式的此值将被忽略;窗口不会调整大小,但会保持全屏。已弃用 调用Window.setDecorFitsSystemWindows(boolean)并传入false,并在您的根内容视图上安装一个OnApplyWindowInsetsListener,以适应Type.ime()类型的内衬。@Deprecatedpublic static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;//软输入模式的调整选项:设置为在显示输入法时让窗口平移,这样它就不需要处理调整大小,只需由框架平移以确保当前输入焦点可见。这不能与SOFT_INPUT_ADJUST_RESIZE结合使用;如果这两个都没有设置,那么系统将尝试根据窗口的内容选择一个。public static final int SOFT_INPUT_ADJUST_PAN = 0x20;//软输入模式的调整选项:设置为在显示输入法时让窗口平移,这样它就不需要处理调整大小,只需由框架平移以确保当前输入焦点可见。这不能与SOFT_INPUT_ADJUST_RESIZE结合使用;如果这两个都没有设置,那么系统将根据窗口的内容尝试选择一个调整方式。public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;//软输入模式的调整选项:设置为让窗口在显示输入法时不进行调整。窗口不会调整大小,也不会平移以使其焦点可见。public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;}
}
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Window操作
Window的操作包含对Window的添加、更新和删除的操作。WindowManager对Window进行管理,Window的操作最终都交由WMS进行处理。我们知道Window分为Application Window、Sub Window和System Window,不同窗口操作过程有所不同。以系统为例学习其操作过程,首先是添加过程。
系统窗口的添加过程
针对不同的系统窗口添加过程也有所区别,以SystemUI中StatusBar为例,系统状态栏显示通知icon、系统icon、时间、电量等信息,与我们使用息息相关。
首先来看为StatusBar添加Window的逻辑,如下所示,CentralSurfacesImpl是SystemUI中用于处理通知栏、锁屏界面和状态栏初始化和协调的类,关于SystemUI之前的文章SystemUI解析有聊过,CentralSurfacesImpl会被SystemUIService启动并调用start
方法。其中就有为StatusBar添加Window的实现方法createAndAddWindows
,makeStatusBarView
方法用于构建StatusBar的视图等逻辑。继续分析StatusBarWindowController的attach
方法,StatusBarWindowController封装了状态栏窗口状态管理的所有逻辑。
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@SysUISingleton
public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {private final StatusBarWindowController mStatusBarWindowController;@Overridepublic void start() {...try {result = mBarService.registerStatusBar(mCommandQueue);} catch (RemoteException ex) {ex.rethrowFromSystemServer();}createAndAddWindows(result);}private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {makeStatusBarView(result);mNotificationShadeWindowController.attach();mStatusBarWindowController.attach();}
}
首先创建LayoutParams来配置StatusBar的属性,包含Width、Height、Type、Flag、Gravity、SoftInputMode等。mStatusBarWindowView是被注入的,具体的实现在StatusBarWindowModule中。最终通过addView
方法将StatusBarView添加到Window中,addView
方法定义在WindowManager类的父类ViewManager中,addView
方法实际是在WindowManagerImpl中实现的,而前面提到过,addView
方法又会调用WindowManagerGlobal的addView
方法。
//packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@SysUISingleton
public class StatusBarWindowController {private final WindowManager mWindowManager;private final ViewGroup mStatusBarWindowView;private WindowManager.LayoutParams mLp;public void attach() {// Now that the status bar window encompasses the sliding panel and its// translucent backdrop, the entire thing is made TRANSLUCENT and is// hardware-accelerated.Trace.beginSection("StatusBarWindowController.getBarLayoutParams");mLp = getBarLayoutParams(mContext.getDisplay().getRotation());Trace.endSection();mWindowManager.addView(mStatusBarWindowView, mLp);...}
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);lp.paramsForRotation = new WindowManager.LayoutParams[4];for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);}return lp;}private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {//StatusBar 高度int height = SystemBarUtils.getStatusBarHeightForRotation(mContext, rotation);WindowManager.LayoutParams lp = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,height,WindowManager.LayoutParams.TYPE_STATUS_BAR,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,PixelFormat.TRANSLUCENT);lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;lp.token = new Binder();lp.gravity = Gravity.TOP;lp.setFitInsetsTypes(0 /* types */);lp.setTitle("StatusBar");lp.packageName = mContext.getPackageName();lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;//处理系统手势final InsetsFrameProvider gestureInsetsProvider =new InsetsFrameProvider(mInsetsSourceOwner, 0, mandatorySystemGestures());//显示缺口安全区域设置最小Insetsfinal int safeTouchRegionHeight = mContext.getResources().getDimensionPixelSize(com.android.internal.R.dimen.display_cutout_touchable_region_size);if (safeTouchRegionHeight > 0) {gestureInsetsProvider.setMinimalInsetsSizeInDisplayCutoutSafe(Insets.of(0, safeTouchRegionHeight, 0, 0));}lp.providedInsets = new InsetsFrameProvider[] {new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()).setInsetsSize(getInsets(height)),new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()).setInsetsSize(getInsets(height)),gestureInsetsProvider};return lp;}
}
addView
方法中首先会对参数进行检查,不匹配则抛出异常,然后判断如果当前窗口要做为子窗口,就会根据父窗口对子窗口的LayoutParams进行调整,接着创建ViewRootImpl,将调整的wparams重新设置给StatusBarView,mViews包含View列表,mRoots包含ViewRootImpl列表,mParams中包含布局参数列表,分别添加到这些数组中。最后调用ViewRootImpl的setView
方法。
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public final class WindowManagerGlobal {//添加window返回的各种状态public static final int ADD_OKAY = 0;public static final int ADD_BAD_APP_TOKEN = -1;public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;public static final int ADD_NOT_APP_TOKEN = -3;public static final int ADD_APP_EXITING = -4;public static final int ADD_DUPLICATE_ADD = -5;public static final int ADD_STARTING_NOT_NEEDED = -6;public static final int ADD_MULTIPLE_SINGLETON = -7;public static final int ADD_PERMISSION_DENIED = -8;public static final int ADD_INVALID_DISPLAY = -9;public static final int ADD_INVALID_TYPE = -10;public static final int ADD_INVALID_USER = -11;@UnsupportedAppUsageprivate final ArrayList<View> mViews = new ArrayList<View>();@UnsupportedAppUsageprivate final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();@UnsupportedAppUsageprivate final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {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;if (parentWindow != null) {parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {// If there's no parent, then hardware acceleration for this view is// set from the application's hardware acceleration setting.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;...IWindowSession windowlessSession = null;// If there is a parent set, but we can't find it, it may be coming// from a SurfaceControlViewHost hierarchy.if (wparams.token != null && panelParentView == null) {for (int i = 0; i < mWindowlessRoots.size(); i++) {ViewRootImpl maybeParent = mWindowlessRoots.get(i);if (maybeParent.getWindowToken() == wparams.token) {windowlessSession = maybeParent.getWindowSession();break;}}}if (windowlessSession == null) {root = new ViewRootImpl(view.getContext(), display);} else {root = new ViewRootImpl(view.getContext(), display,windowlessSession, new WindowlessWindowLayout());}view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);// do this last because it fires off messages to start doing thingstry {root.setView(view, wparams, panelParentView, userId);} catch (RuntimeException e) {final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);// BadTokenException or InvalidDisplayException, clean up.if (viewIndex >= 0) {removeViewLocked(viewIndex, true);}throw e;}}}@UnsupportedAppUsagepublic static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();IWindowManager windowManager = getWindowManagerService();sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}});} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return sWindowSession;}}
}
ViewRootImpl是View中的最高层级,属于所有View的根,在Android View View工作原理中我们也提到过这部分,通过调用IWindowSession的addToDisplayAsUser
方法,IWindowSession是Binder对象,对应的服务端实现为Session,通常,与WindowManager交互的每个进程都有一个Session对象,在启动时有提到它。这里需要注意,之前的逻辑均在SystemUI进程,addToDisplayAsUser
方法会运行在WMS所在的进程(SystemServer进程)。
//frameworks/base/core/java/android/view/ViewRootImpl.java
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {@UnsupportedAppUsagefinal IWindowSession mWindowSession;public ViewRootImpl(Context context, Display display) {this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());}public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {...try {Rect attachedFrame = new Rect();final float[] compatScale = { 1f };//将该window添加到屏幕,通过AIDL通知WindowManagerService添加windowres = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,mTempControls, attachedFrame, compatScale);...} catch (RemoteException | RuntimeException e) {...throw new RuntimeException("Adding window failed", e);} finally {if (restore) {attrs.restore();}}}
}
addToDisplayAsUser
方法中调用了WMS的addWindow
方法。
//frameworks/base/services/core/java/com/android/server/wm/Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {final WindowManagerService mService;@Overridepublic int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,float[] outSizeCompatScale) {return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,requestedVisibleTypes, outInputChannel, outInsetsState, outActiveControls,outAttachedFrame, outSizeCompatScale);}
}
addWindow
方法返回的是添加的各种状态,诸如ADD_OKAY
、ADD_BAD_APP_TOKEN
、ADD_INVALID_TYPE
等,这些状态被定义在WindowManagerGlobal中。首先调用WindowManagerPolicy的checkAddPermission
方法检查权限,如果权限不OK则不会执行后续代码。接着通过displayId获取DisplayContent对象,它用于跟踪特定Display的WindowState和其他相关内容的实用程序类,描述一块屏幕,隶属于同一个DisplayContent的窗口会被显示在同一个屏幕中。Display提供有关逻辑显示的大小和密度的信息,如果DisplayContent为null,则返回无效 display的状态。然后判断窗口类型是否是子窗口类型,如果是那就判断父窗口是否存在或者窗口类型不对也会返回ADD_BAD_SUBWINDOW_TOKEN
,前面主要做了各种窗口类型、Display、用户等检查。
通过DisplayContent的getWindowToken
方法获取WindowToken,如果有父窗口就将父窗口的type赋值给rootType,如果当前的token为null,有父窗口直接使用父窗口token,否则判断是否有监听windowContextToken,这里的token对象是IBinder,不是WindowToken,直接创建WindowToken,并给参数赋值。接着判断rootType,如果是TYPE_INPUT_METHOD
、TYPE_VOICE_INTERACTION
、TYPE_WALLPAPER
、TYPE_ACCESSIBILITY_OVERLAY
、TYPE_TOAST
、TYPE_QS_DIALOG
,都会返回ADD_BAD_APP_TOKEN
状态,因为它们不允许WindowToken为null。
创建WindowState,它保存窗口的所有状态信息,WindowManager中的窗口,然后调用DisplayPolicy的adjustWindowParamsLw
方法,会根据窗口type对窗口的LayoutParams进行修改。然后将WindowState添加到mWindowMap中,最后将WindowState添加到该WindowState对应的WindowToken中,实际保存在WindowToken的父类WindowContainer中,这样WIndowToken就包含了同一个组件的WindowState.
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {@NonNullfinal RootWindowContainer mRoot;//WindowState用于保存窗口的信息,用来描述一个窗口,mWindowMap用于保存各种窗口集合。final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,float[] outSizeCompatScale) {...int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);if (res != ADD_OKAY) {return res;}...//窗口类型 final int type = attrs.type; synchronized (mGlobalLock) {final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);if (displayContent == null) {ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "+ "not exist: %d. Aborting.", displayId);return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {parentWindow = windowForClientLocked(null, attrs.token, false);if (parentWindow == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}}/**各种检查**/...ActivityRecord activity = null;final boolean hasParent = parentWindow != null;//WindowManager中一组相关窗口的容器。这通常是一个AppWindowToken,它是用于显示窗口的Activity的句柄。对于嵌套窗口,会为父窗口创建一个WindowToken来管理其子窗口。当应用想要向WMS申请创建一个窗口,则需要向WMS出示有效的WIndowToken,应用程序中每个Activity都对应一个WindowToken,具体实现类为ActivityRecord。WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// If this is a child window, we want to apply the same type checking rules as the// parent window type.final int rootType = hasParent ? parentWindow.mAttrs.type : type;boolean addToastWindowRequiresToken = false;final IBinder windowContextToken = attrs.mWindowContextToken;if (token == null) {if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,rootType, attrs.token, attrs.packageName)) {return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (hasParent) {// Use existing parent window token for child windows.token = parentWindow.mToken;//WindowContextListenerController是一个用于注册/注销 WindowContext 的 WindowContainerListener 的控制器。 当创建 WindowContext 时,它会自动注册监听器。 当 WindowContext 通过 WindowManager.addView(View, ViewGroup.LayoutParams) 向屏幕添加第一个窗口时,WindowManagerService 会更新 WindowContextListenerImpl,通过这个控制器监听相应的 WindowToken。 当 WindowContext 被垃圾回收时,它会通过 WindowManagerService.detachWindowContext(IBinder) 注销之前注册的监听器。WindowManagerService 还负责移除 WindowContext 创建的 WindowToken。 } else if (mWindowContextListenerController.hasListener(windowContextToken)) {// Respect the window context token if the user provided it.final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).setFromClientToken(true).setOptions(options).build();} else {final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();}} else if (rootType >= FIRST_APPLICATION_WINDOW&& rootType <= LAST_APPLICATION_WINDOW) {//这里和前面WindowToken的说明一样,应用程序窗口WindowToken为ActivityRecord.activity = token.asActivityRecord();//...activity和type检查} else if (rootType == TYPE_INPUT_METHOD) {...} if (rootType == TYPE_VOICE_INTERACTION) {...} else if (rootType == TYPE_WALLPAPER) {...} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {...} else if (type == TYPE_TOAST) { ...} else if (token.asActivityRecord() != null) {// It is not valid to use an app token with other system types; we will// instead make a new token for it (as if null had been passed in for the token).attrs.token = null;token = new WindowToken.Builder(this, client.asBinder(), type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).build();}final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();displayPolicy.adjustWindowParamsLw(win, win.mAttrs);attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid,callingPid);win.setRequestedVisibleTypes(requestedVisibleTypes);...//创建输入通道,用来接收按键事件final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if (openInputChannels) {win.openInputChannel(outInputChannel);}...win.mSession.onWindowAdded(win);mWindowMap.put(client.asBinder(), win);win.initAppOpsState();...win.mToken.addWindow(win);displayPolicy.addWindowLw(win, attrs);displayPolicy.setDropInputModePolicy(win, win.mAttrs);...}return res;} private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {if (token != null) {final WindowToken wToken = mRoot.getWindowToken(token);if (wToken != null) {return wToken.getDisplayContent();}}return mRoot.getDisplayContentOrCreate(displayId);}final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {WindowState win = mWindowMap.get(client);if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);if (win == null) {if (throwOnError) {throw new IllegalArgumentException("Requested window " + client + " does not exist");}ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,Debug.getCallers(3));return null;}if (session != null && win.mSession != session) {if (throwOnError) {throw new IllegalArgumentException("Requested window " + client + " is in session "+ win.mSession + ", not " + session);}ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,Debug.getCallers(3));return null;}return win;}
}
窗口添加完成后,中间通过ViewRootImpl去完成画布Surface的创建,窗口测量、布局、绘制,最终经过SurfaceFlinger合成,显示到窗口上,这部分后续会进行新的篇幅进行学习。
更新过程
Window的更新过程和Window添加过程类似,需要调用ViewManager的updateViewLayout
方法,具体实现在WindowManagerImpl中实现,它又会调用WindowManagerGlobal的updateViewLayout
方法,首先将更新的params设置给View,然后根据索引找到窗口的ViewRootImpl,然后更新布局参数列表,最后调用ViewRootImpl的setLayoutParams
方法将参数设置进去。
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public final class WindowManagerGlobal {@UnsupportedAppUsageprivate final ArrayList<View> mViews = new ArrayList<View>();@UnsupportedAppUsageprivate final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();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);}}private int findViewLocked(View view, boolean required) {final int index = mViews.indexOf(view);if (required && index < 0) {throw new IllegalArgumentException("View=" + view + " not attached to window manager");}return index;}
}
ViewRootImpl的setLayoutParams
方法调用scheduleTraversals
方法,Choreographer的postCallback
方法用于发起添加回调,这个添加的回调会在下一帧被渲染时执行,TraversalRunnable中调用doTraversal
方法,接着调用 performTraversals
方法,这个方法在Android View View工作原理中我们也提到了,首先relayoutWindow
方法内部有IwindowSession的relayout
方法来更新Window视图,最终会调用WMS的relayoutWindow
方法,接着调用performMeasure
、performLayout
、performDraw
方法,内部分别进行View的测量、布局、绘制流程。
//frameworks/base/core/java/android/view/ViewRootImpl.java
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {//用于接收显示系统的Vsync信号,在下一帧渲染时控制执行一些操作。WMS使用它负责驱动所有窗口动画、屏幕旋转动画、墙纸动画的渲染。final Choreographer mChoreographer;final IWindowSession mWindowSession;final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}}public void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {...scheduleTraversals();}@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}}void doTraversal() {...performTraversals();}private void performTraversals() {...int relayoutResult = 0;relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);...performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);...performLayout(lp, mWidth, mHeight);...if (!performDraw(mActiveSurfaceSyncGroup)) {handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,mPendingTransaction, mLastPerformDrawSkippedReason);}} private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {final boolean relayoutAsync;if (relayoutAsync) {mWindowSession.relayoutAsync(mWindow, params,requestedWidth, requestedHeight, viewVisibility,insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,mLastSyncSeqId);} else {relayoutResult = mWindowSession.relayout(mWindow, params,requestedWidth, requestedHeight, viewVisibility,insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,mTempInsets, mTempControls, mRelayoutBundle);mRelayoutRequested = true;}
}
WMS中relayoutWindow
方法首先会通过windowForClientLocked
方法从WindowMap中获取WindowState,接着将从应用端请求的尺寸保存到WindowState中,通过adjustWindowParamsLw
方法调整窗口属性和类型,设置窗口可见,然后创建SurfaceControl,通过performSurfacePlacement
方法计算窗口大小,更新焦点,将计算好的窗口尺寸返回给应用端。这样就完成了Window的更新。
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();final WindowSurfacePlacer mWindowPlacerLocked;public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,int lastSyncSeqId, ClientWindowFrames outFrames,MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,Bundle outSyncIdBundle) {synchronized (mGlobalLock) {final WindowState win = windowForClientLocked(session, client, false);}}final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {WindowState win = mWindowMap.get(client);...WindowStateAnimator winAnimator = win.mWinAnimator;final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();if (viewVisibility != View.GONE) {win.setRequestedSize(requestedWidth, requestedHeight);}...if (attrs != null) {displayPolicy.adjustWindowParamsLw(win, attrs);attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);}...win.setViewVisibility(viewVisibility);if (shouldRelayout && outSurfaceControl != null) {try {result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);}mWindowPlacerLocked.performSurfacePlacement(true /* force */);if (focusMayChange) {if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {imMayMove = false;}}if (outFrames != null && outMergedConfiguration != null) {win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,false /* useLatestConfig */, shouldRelayout);// Set resize-handled here because the values are sent back to the client.win.onResizeHandled();} return result;}
}
删除过程
和之前一样,删除Window首先调用WindowManagerGlobal的removeView
方法,首先找到View在列表中的索引,然后找到对应的View,接着调用removeViewLocked
方法,获取ViewRootImpl,onWindowDismissed
方法结束对View输入法相关的逻辑,接着调用其die
方法。
public final class WindowManagerGlobal {@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)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 (root != null) {root.getImeFocusController().onWindowDismissed();}boolean deferred = root.die(immediate);if (view != null) {view.assignParent(null);if (deferred) {mDyingViews.add(view);}}}
}
mIsInTraversal是在performTraversals
中赋值为true,执行完设置为false,doDie
方法中首先检查线程的正确性,是否是原始创建View的线程,如果有子View就会调用dispatchDetachedFromWindow
方法来销毁View,内部会调用WindowSession的remove
方法。然后判断有子View并且不是第一次被添加,则更新布局参数后调用finishDrawing
完成绘制操作,最终调用WindowManagerGlobal的doRemoveView
方法,删除列表中的元素。
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {boolean die(boolean immediate) {// Make sure we do execute immediately if we are in the middle of a traversal or the damage// done by dispatchDetachedFromWindow will cause havoc on return.if (immediate && !mIsInTraversal) {doDie();return false;}if (!mIsDrawing) {destroyHardwareRenderer();} else {Log.e(mTag, "Attempting to destroy the window while drawing!\n" +" window=" + this + ", title=" + mWindowAttributes.getTitle());}mHandler.sendEmptyMessage(MSG_DIE);return true;}void doDie() {checkThread();synchronized (this) {if (mAdded) {dispatchDetachedFromWindow();}if (mAdded && !mFirst) {if (mView != null) {if (mWindowAttributesChanged || viewVisibilityChanged) {// If layout params have been changed, first give them// to the window manager to make sure it has the correct// animation info.try {if ((relayoutWindow(mWindowAttributes, viewVisibility, false)& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {mWindowSession.finishDrawing(mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE);}}destroySurface();}}}WindowManagerGlobal.getInstance().doRemoveView(this);}void checkThread() {Thread current = Thread.currentThread();if (mThread != current) {throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views."+ " Expected: " + mThread.getName()+ " Calling: " + current.getName());}} void dispatchDetachedFromWindow() {...try {mWindowSession.remove(mWindow.asBinder());} catch (RemoteException e) {}...}
}
它调用的是WMS的removeClientToken
方法。
//frameworks/base/services/core/java/com/android/server/wm/Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {final WindowManagerService mService;@Overridepublic void remove(IBinder clientToken) {mService.removeClientToken(this, clientToken);}
}
首先它查找到WindowState对象,然后调用其removeIfPossible
方法。
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {void removeClientToken(Session session, IBinder client) {synchronized (mGlobalLock) {WindowState win = windowForClientLocked(session, client, false);if (win != null) {win.removeIfPossible();return;}// Remove embedded window map if the token belongs to an embedded windowmEmbeddedWindowController.remove(client);}}final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {WindowState win = mWindowMap.get(client);if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);if (win == null) {if (throwOnError) {throw new IllegalArgumentException("Requested window " + client + " does not exist");}ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,Debug.getCallers(3));return null;}if (session != null && win.mSession != session) {if (throwOnError) {throw new IllegalArgumentException("Requested window " + client + " is in session "+ win.mSession + ", not " + session);}ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,Debug.getCallers(3));return null;}return win;}
}
removeIfPossible
方法进行多个条件过滤,满足则推迟删除操作,如View正在运行动画,那需要等动画完成再进行删除,然后执行removeImmediately
方法,onWindowRemoved
方法从mSessions中删除并清除Session,这其中也将对应的SurfaceSession进行了清除,SurfaceSession是SurfaceFlinger的一个链接,通过这个连接可以创建一个或多个Surface渲染到屏幕上,WMS的postWindowRemoveCleanupLocked
方法进行了一些参数的清理工作。这样就完成Window的删除。
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,InsetsControlTarget, InputTarget {@Overridevoid removeIfPossible() {...removeImmediately();...}@Overridevoid removeImmediately() {...final DisplayContent dc = getDisplayContent();mWinAnimator.destroySurfaceLocked(getSyncTransaction());dc.getDisplayPolicy().removeWindowLw(this);mSession.onWindowRemoved(this);mWmService.postWindowRemoveCleanupLocked(this);}
}
动画执行
窗口动画的本质是对窗口添加一个变换,通过WMS的scheduleAnimationLocked
方法,接着调用WindowAnimator的scheduleAnimation
方法,其内部还是调用了Choreographer的postFrameCallback
方法。
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs final WindowAnimator mAnimator;void scheduleAnimationLocked() {mAnimator.scheduleAnimation();}
}
//frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
public class WindowAnimator {private Choreographer mChoreographer;final Choreographer.FrameCallback mAnimationFrameCallback;WindowAnimator(final WindowManagerService service) {mService = service;mContext = service.mContext;mPolicy = service.mPolicy;mTransaction = service.mTransactionFactory.get();service.mAnimationHandler.runWithScissors(() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);mAnimationFrameCallback = frameTimeNs -> {synchronized (mService.mGlobalLock) {mAnimationFrameCallbackScheduled = false;animate(frameTimeNs);if (mNotifyWhenNoAnimation && !mLastRootAnimating) {mService.mGlobalLock.notifyAll();}}};}void scheduleAnimation() {if (!mAnimationFrameCallbackScheduled) {mAnimationFrameCallbackScheduled = true;mChoreographer.postFrameCallback(mAnimationFrameCallback);}}private void cancelAnimation() {if (mAnimationFrameCallbackScheduled) {mAnimationFrameCallbackScheduled = false;mChoreographer.removeFrameCallback(mAnimationFrameCallback);}}
}
public final class Choreographer {public void postFrameCallback(FrameCallback callback) {postFrameCallbackDelayed(callback, 0);}public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {if (callback == null) {throw new IllegalArgumentException("callback must not be null");}postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);}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);}}}private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME:doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());break;case MSG_DO_SCHEDULE_VSYNC:doScheduleVsync();break;case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);break;}}}void doScheduleCallback(int callbackType) {synchronized (mLock) {if (!mFrameScheduled) {final long now = SystemClock.uptimeMillis();if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {scheduleFrameLocked(now);}}}}private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {mFrameScheduled = true;if (USE_VSYNC) {if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame on vsync.");}if (isRunningOnLooperThreadLocked()) {scheduleVsyncLocked();} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);msg.setAsynchronous(true);mHandler.sendMessageAtFrontOfQueue(msg);}} else {final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}}void doScheduleVsync() {synchronized (mLock) {if (mFrameScheduled) {scheduleVsyncLocked();}}}void doFrame(long frameTimeNanos, int frame,DisplayEventReceiver.VsyncEventData vsyncEventData) {...mFrameInfo.markInputHandlingStart();//处理输入事件doCallbacks(Choreographer.CALLBACK_INPUT, frameIntervalNanos);mFrameInfo.markAnimationsStart();//处理动画doCallbacks(Choreographer.CALLBACK_ANIMATION, frameIntervalNanos);//处理布局doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameIntervalNanos);mFrameInfo.markPerformTraversalsStart();doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameIntervalNanos);doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos); }
}
总结
WMS管理系统中的所有Window,且分配屏幕触摸事件。本文分析了WMS及相关的Window属性、Window操作、动画执行过程,后续会结合此文章继续分析屏幕渲染相关文章。