1 Window
、WindowManager
和 WindowManagerService(WMS)
Window
是一个抽象类,具体的实现类为 PhoneWindow
,它对 View
进行管理。WindowManager
是一个接口类,继承自接口 ViewManager
,它是用来管理 Window
的,它的实现类为 WindowManagerImpl
,如果想要对 Window/View
进行添加、更新和删除操作就可以使用 WindowManager
。
WindowManagerService
是 Android
系统中的重要服务,管理所有窗口,也是输入事件的中转站。WindowManagerService
继承自 IWindowManager.Stub
,与 WindowManager
通过 Binder
来进行跨进程通信。
WMS
作为系统服务有很多 API
是无法暴露给 WindowManager
的,这一点与 AMS
和 ActivityManager
的关系有些类似。
Window
、WindowManager
和 WMS
的关系可以简略的用下图来表示:
Window
包含了 View
并对 View
进行管理,Window
用虚线来表示是因为 Window
是一个抽象概念,用来描述一个窗口,并不是真实存在的。WindowManager
用来管理 Window
,而 WindowManager
所提供的功能最终会由 WMS
进行处理。
以下是 WindowManager
的关联类:
public abstract class Window { }
public class PhoneWindow extends Window implements MenuBuilder.Callback { }
public interface ViewManager {
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
public interface WindowManager extends ViewManager { }
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
}
public final class WindowManagerGlobal { }
interface IWindowManager { }
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { }
**WindowManager
是一个接口类,继承自接口 ViewManager
,ViewManager
中定义了 3
个方法,分别用来添加、更新和删除 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);
}
**WindowManager
也继承了这些方法,而这些方法传入的参数都是 View
类型,说明 Window
是以 View
的形式存在的。WindowManager
在继承 ViewManager
的同时,又加入很多功能,包括 Window
的类型和层级相关的常量、内部类以及一些方法,其中有两个方法是根据 Window
的特性加入的,**如下所示:
// /frameworks/base/core/java/android/view/WindowManager.java
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
WindowManager.getDefaultDisplay()
方法能够得知这个 WindowManager
实例将 Window
添加到哪个屏幕上了,换句话说,就是的到 WindowManager
所管理的屏幕(Display
)。WindowManager.removeViewImmediate
方法则固定在这个方法换回钱要立即执行 View.onDetachedFromWindow()
,来完成传入的 View
的相关的销毁工作。
2 WindowManagerService
的创建过程
WindowManagerService
是在 system_server
进程中创建,首先来看 SystemServer.main()
方法:
// /frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
在 SystemServer.main()
中调用了 SystemServer().run()
方法:
// /frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
try {
...
Looper.prepareMainLooper(); // 创建消息 Looper
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
// Initialize native services.
System.loadLibrary("android_servers"); // 1 创建动态库 libandroid_servers.so
...
// Initialize the system context. 创建系统的 Context
createSystemContext();
// 2 Create the system service manager. 创建 SystemServiceManager
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Prepare the thread pool for init tasks that can be parallelized
SystemServerInitThreadPool.get();
} finally {
traceEnd(); // InitBeforeStartServices
}
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices(); // 3 启动引导服务
startCoreServices(); // 4 启动核心服务
startOtherServices(); // 5 启动其他服务
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
...
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在注释 1
处加载了动态库 libandroid_servers.so
。接下来在注释 2
处创建 SystemServiceManager
,它会对系统的服务进行创建、启动和生命周期管理。在注释 3
处的 startBootstrapServices()
方法中用 SystemServiceManager
启动了 ActivityManagerService
、PowerManagerService
、PackageManagerService
等服务。在注释 4
处的 startCoreServices()
方法中启动了 DropBoxManagerService
、BatteryService
、UsageStateService
和 WebViewUpdateService
。在注释 5
处的 startOtherServices()
方法中启动了 CameraService
、AlarmManagerService
、VrManagerService
等服务。从注释 3
、4
、5
处的方法可以看出,官方把系统服务分为三种类型,分别是引导服务、核心服务和其他服务,其中其他服务是一些非要紧的和不需要立即启动的服务,WindowManagerService
就是其他服务的一种。
以下是 SystemServer.startOtherServices()
方法的源代码:
// /frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
...
traceBeginAndSlog("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(),
mActivityManagerService.mActivityTaskManager); // 1
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); // 2
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); // 3
traceEnd();
traceBeginAndSlog("SetWindowManagerService");
mActivityManagerService.setWindowManager(wm);
traceEnd();
traceBeginAndSlog("WindowManagerServiceOnInitReady");
wm.onInitReady(); // 4
traceEnd();
...
try {
wm.displayReady(); // 5
} catch (Throwable e) {
reportWtf("making display ready", e);
}
traceEnd();
...
traceBeginAndSlog("MakeWindowManagerServiceReady");
try {
wm.systemReady(); // 6
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
traceEnd();
...
}
SystemServer.startOtherServices()
方法用于启动其他服务,其他服务大概有 100
个左右。在注释 1
处执行了 WindowManagerService.main
方法,其内部会创建 WindowManagerService
,需要注意的是WindowManagerService.main
方法中传入的其中一个参数是 InputManagerService
,WindowManagerService
是输入事件的中转站,其内部包含 InputManagerService
并不奇怪。
WindowManagerService.main
方法是运行在 SystemServer.run()
方法中的,换句话说是运行在 system_server
进程中的。注释 2
和注释 3
处分别将 WindowManagerService
和 InputManagerService
注册到 ServiceManager
中,这样,如果某个客户端想要使用 WindowManagerService
,就需要到 ServiceManager
中查询信息,然后根据信息和 WindowManagerService
所在的进程建立通道,客户端就可以使用 WindowManagerService
了。
首先来看 WindowManagerService.main
方法:
// /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
return main(context, im, showBootMsgs, onlyCore, policy, atm,
SurfaceControl.Transaction::new);
}
@VisibleForTesting
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy,
ActivityTaskManagerService atm,
TransactionFactory transactionFactory) {
DisplayThread.getHandler().runWithScissors(() -> // 1
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory), 0); // 2
return sInstance;
}
注释 2
处运行在 Runnable.run
方法中,在这里创建了 WindowManagerService
实例。
在注释 1
处调用了 DisplayThread.getHandler()
方法来得到 Handler
实例。DisplayThread
是一个单例的前台线程,这个线程用来处理低需要低延时显示的相关操作,并只能由 WindowManager
、DisplayManager
和 InputManager
实时执行快速操作。
// frameworks/base/services/core/java/com/android/server/DisplayThread.java
public static Handler getHandler() {
synchronized (DisplayThread.class) {
ensureThreadLocked();
return sHandler;
}
}
DisplayThread.getHandler().runWithScissors(...)
方法的第一个参数是 Runnable
,第二个参数是 timeout
,这里传入的是 0
。通过以上可以知道,WindowManagerService
的创建是运行在 android.display
线程中的。 以下是 Handler.runWithScissors(...)
方法的源代码:
// /frameworks/base/core/java/android/os/Handler.java
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) { // 1
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
在注释 1
处根据每一个线程只有一个 Looper
的原理来判断当前线程(system_server
线程)是否是 Handler
所指的线程(android.display
),如果是则直接执行 Runnable.run
方法,如果不是则调用 BlockingRunnable.postAndWait
方法,并将当前线程的 Runnable
作为参数传进去,BlockingRunnable
是 Handler
的内部类,代码如下所示:
// /frameworks/base/core/java/android/os/Handler.java
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run(); // 1
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) { // 2
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait(); // 3
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
在注释 2
处将当前的 BlockingRunnable
添加到 Handler
的任务队列中。Handler.runWithScissors
方法的第二个参数是 0
,也就是 timeout == 0
,这样就会到 else
的相关操作中。如果 mDone == false
的话会调用注释 3
处的 wait()
方法使得当前线程(system_server
线程)进入等待状态,这里等待的是 android.display
线程。在注释 1
处执行了 Runnable.run
方法(android.display
线程),执行完毕后,将 finally
代码块中的 mDone
设置为 true
,并调用 notifyAll()
方法唤醒处于等待状态的线程(system_server
线程)。
因此,在这段过程中,android.display
的优先级比较高,system_server
线程等待的就是 android.display
线程,一直到 android.display
线程执行完毕再执行 system_server
线程。 android.display
线程内部执行了 WindowManagetService
的创建。
回到 SystemServer.run()
方法中,
// /frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
...
traceBeginAndSlog("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(),
mActivityManagerService.mActivityTaskManager); // 1
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); // 2
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); // 3
traceEnd();
traceBeginAndSlog("SetWindowManagerService");
mActivityManagerService.setWindowManager(wm);
traceEnd();
traceBeginAndSlog("WindowManagerServiceOnInitReady");
wm.onInitReady(); // 4
traceEnd();
...
try {
wm.displayReady(); // 5
} catch (Throwable e) {
reportWtf("making display ready", e);
}
traceEnd();
...
traceBeginAndSlog("MakeWindowManagerServiceReady");
try {
wm.systemReady(); // 6
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
traceEnd();
...
}
注释 5
处用来初始化屏幕显示信息。注释 6
处则用来通知 WindowManagerService
初始化工作已经完成。在注释 4
处调用了 WindowManagerService.onInitReady()
方法,以下是相关实现:
// /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void onInitReady() {
initPolicy(); // 1
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
openSurfaceTransaction();
try {
createWatermarkInTransaction();
} finally {
closeSurfaceTransaction("createWatermarkInTransaction");
}
showEmulatorDisplayOverlayIfNeeded();
}
WindowManagerPolicy mPolicy;
private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); // 2
}
}, 0);
}
注释 2
处初始化了窗口的管理策略的接口类 WindowManagerPolicy(WMP)
,用来定义一个窗口策略所遵循的通用规范。WindowManagerPolicy(WMP).init(...)
方法在 android.ui
线程中,它的优先级要高于 initPolicy()
方法所在的android.display
线程。在这里,android.display
线程要等 WindowManagerPolicy.init(...)
方法执行完毕后才会被唤醒。
3 创建 Window
以及获取WindowManager
我们已经知道,Window
是一个抽象类,它的具体实现类为 PhoneWindow
。在 Activity
启动过程中会调用 ActivityThread.performLaunchActivity
方法,在此方法中又会调用 Activity.attach
方法,PhoneWindow
就是在 Activity.attach
方法中创建的。
以下是 Activity.attach(...)
的相关代码:
// /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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window); // 1
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); // 2
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
在注释 1
处创建了 PhoneWindow
,在注释 2
出调用了 PhoneWindow.setWindowManager(...)
方法,这个方法在 PhoneWindow
的父类 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
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); // 1
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); // 2
}
如果传入的 wm == null
,就会在注释 1
处调用 Context.getSystemService
方法,并传入服务名称 Context.WINDOW_SERVICE
(public static final String WINDOW_SERVICE = "window"
)。Context.getSystemService(...)
方法具体在 ContextImpl
中实现,如下所示:
// /frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
在 ContextImpl.getSystemService
方法中会调用 SystemServiceRegister.getSystemService
方法:
// /frameworks/base/core/java/android/app/SystemServiceRegistry.java
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
new HashMap<Class<?>, String>();
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
SYSTEM_SERVICE_FETCHERS
是一个 HashMap
类型的数据,它用来存储服务的名称,那么传入的 Context.WINDOW_SERVICE
到底对应着什么?接着往下看:
// /frameworks/base/core/java/android/app/SystemServiceRegistry.java
private SystemServiceRegistry() { }
static {
...
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx); // 1
}});
...
}
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
在 SystemServieRegistry
的静态代码块中会调用多个 registerService
方法。registerService
方法内部会将传入的服务的名称存入到 SYSTEM_SERVICE_FETCHERS
中。从注释 1
处可以看出,传入的 Context.WINDOW_SERVICE
对应的就是 WindowManagerImpl
实例,因此得出结论,Context.getSystemService
方法得到的是 WindowManagerImpl
实例。再回到 Window.setWindowManager
方法,在注释 1
处得到 WindowManagerImpl.createLocalWindowManager
方法:
// /frameworks/base/core/java/android/view/WindowManagerImpl.java
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
WindowManagerImpl.createLocalWindowManager
方法同样也是创建 WindowManagerImpl
,不同的是这次创建 WindowManagerImpl
时将创建它的 Window
作为参数传了进来,这样 WindowManagerImpl
就持有了 Window
的引用,可以对 Window
进行操作。
4 Window
的属性
为了方便的管理 Window
,需要制定一些“协议”,这些“协议”就是 Window
的属性,它们被定义在 WindowManager
的内部类 LayoutParams
中。Window
的属性有很多,与开发密切的有 3
种,它分别是 Type
(Window
的类型)、Flag
(Window
的标志)和 SoftInputMode
(软键盘模式)。
4.1 Window
的类型和显示次序
Window
的类型有很多,比如应用程序窗口、系统错误窗口、输入法窗口、PopupWindow
、Toast
、Dialog
等。总的来说,Window
分为三大类型,分别是 Application Window
(应用程序窗口)、Sub Window
(子窗口)、System Window
(系统窗口),每个类型中又包含了很多种类型,它们都定义在 WindowManager
的静态内部类 LayoutParams
中。
4.1.1 应用程序窗口
Activity
是一个典型的应用程序窗口, 应用程序窗口包含的类型如下:
// /frameworks/base/core/java/android/view/WindowManager.java
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
public static final int FIRST_APPLICATION_WINDOW = 1; // 1
// 窗口的基础值,其他的窗口值要大于这个值
public static final int TYPE_BASE_APPLICATION = 1;
public static final int TYPE_APPLICATION = 2; // 普通的应用程序窗口类型
// 应用程序启动窗口类型,用于系统在应用程序窗口启动前显示的窗口
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99; // 2
}
应用程序窗口共包含了以上几种 Type
值,其中注释 1
处的 Type
表示应用程序窗口类型初始值,注释 2
处的 Type
表示应用程序窗口类型的结束值,也就是说应用程序窗口的 Type
值范围是 1~99
,这个数值的大小涉及窗口的层级。
4.1.2 子窗口
**子窗口不能独立存在,需要附着其他窗口才可以,PopupWindow
就属于子窗口。**子窗口的类型定义如下:
// /frameworks/base/core/java/android/view/WindowManager.java
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
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;
public static final int LAST_SUB_WINDOW = 1999; // 子窗口类型结束值
}
子窗口的 Type
值的范围为 1000~1999
。
4.1.3 系统窗口
**Toast
、输入窗口、系统音量条窗口、系统错误窗口都属于系统窗口。**系统窗口的类型定义如下所示:
// /frameworks/base/core/java/android/view/WindowManager.java
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; // 搜索条窗口
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; // 通话窗口
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; //系统 ALERT 窗口
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; // 锁屏窗口
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; // Toast 窗口
...
public static final int LAST_SYSTEM_WINDOW = 2999; // 系统窗口欧类型结束值
}
系统窗口的类型值接近 40
个,系统窗口的 Type
值范围为 2000~2999
。
4.1.4 窗口显示次序
当一个进程向 WindowManagerService
申请一个窗口时,WindowManagerService
会为窗口确定显示次序。为了方便窗口显示次序的管理,手机屏幕可以虚拟地用 X
、Y
、Z
轴来表示,其中,Z
轴垂直于屏幕,从屏幕内指向屏幕外,这样确定窗口显示次序也就是确定窗口在 Z
轴上的次序,这个次序称为 Z-Order
。Type
值是 Z-Order
排序的依据。应用程序窗口的 Type
值范围为 1~99
,子窗口 1000~1999
,系统窗口 2000~2999
,在一般情况下,Type
值越大则 Z-Order
排序越靠前,就越靠近用户。
4.2 Window
的标志
Window
的标志也是 Flag
,用于控制 Window
的显示,同样被定义在 WindowManager
的内部类 LayoutParams
中,一共有 20
多个,下面是几个常用的:
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
:只要窗口可见,就允许在开启状态的屏幕上锁定;FLAG_NOT_FOCUSABLE
:窗口不能获得输入焦点,设置该标志的同时,FLAG_NOT_TOUCH_MODAL
也会被设置;FLAG_NOT_TOUCHABLE
:窗口不接收任何触摸事件;FLAG_NOT_TOUCH_MODAL
:将该窗口区域外的触摸事件传递给其他的Window
,而自己只会处理窗口区域内的触摸事件;FLAG_KEEP_SCREEN_ON
:只要窗口可见,屏幕就会一直亮着;FLAG_LAYOUT_NOT_LIMITS
:允许窗口超过屏幕之外;FLAG_FULLSCREEN
:隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示;FLAG_SHOW_WHEN_LOCKED
:窗口可以在锁屏的窗口之上显示;FLAG_IGNORE_CHEEK_PRESSES
:当用户的脸贴近屏幕时(比如打电话),不会去响应此事件;FLAG_TURN_SCREEN_ON
:窗口显示时将屏幕点亮;
设置 Window
的 Flag
有 3
种方法,第一种是通过 Window.addFlags
方法:
Window mWindow = getWindow();
mWindow.addFlag(WindowManager.LayoutParams.FLAG_FULLSCREEN);
第二种通过 Window.setFlags
方法:
Window mWindow = getWindow();
mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_SCREEN);
其实,Window.addFlags
方法的内部会调用 Window.setFlags
方法,以下是相关源码:
// /frameworks/base/core/java/android/view/Window.java
public void addFlags(int flags) {
setFlags(flags, flags);
}
public void setFlags(int flags, int mask) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.flags = (attrs.flags&~mask) | (flags&mask);
mForcedWindowFlags |= mask;
dispatchWindowAttributesChanged(attrs);
}
第三种方法是给 LayoutParams
设置 Flag
,并通过 WindowManager.addView
方法进行添加,如下所示:
WindowManager.LayoutParams mWindowLayoutParams = new WindowManagerParams.LayoutParams();
mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
TextView mTextView = new TextView(this);
mWindowManager.addView(mTextView, mWindowLayoutParams);
4.3 软键盘相关模式
窗口和窗口的叠加是十分常见的场景,但是,如果其中的窗口是软键盘窗口,可能会出现一些问题,比如典型的用户登录界面,默认的情况弹出的软键盘窗口可能会覆盖住输入框下面的按钮。为了使软键盘窗口能偶按照期望来显示,WindowManager
的静态内部类LayoutParams
中定义了软键盘的相关模式,以下是常用的几种:
SOFT_INPUT_STATE_UNSPECIFIED
:没有制定状态,系统会选择一个合适的状态或依赖于主题的设置;SOFT_INPUT_STATE_UNCHANGED
:不会改变软键盘状态;SOFT_INPUT_STATE_HIDDEN
:当用户进入该窗口时,软键盘默认隐藏;SOFT_INPUT_STATE_ALWAYS_HIDDEN
:当窗口获取焦点是,软键盘总是隐藏;SOFT_INPUT_ADJUST_RESIZE
:当软键盘弹出时,窗口会调整大小;SOFT_INPUT_ADJUST_PAN
:当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的;
从以上给出的 SoftInputMode
,可以发现,它们与 AndoridManifest
中的 Activity
的属性 android:windowSoftInputMode
是对应的。因此,除了在 AndroidManifest
中为 Activity
设置 android:windowSoftInputMode
还可以在 Java
代码中为 Window
设置 SoftInputMode
,如下所示:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
5 Window
的操作
WindowManager
对 Window
进行管理,其中包括对 Window
的添加、更新和删除的操作。对于 Window
的操作,最终都是交给 WindowManagerService
来进行处理的。窗口的操作分为两大部分,一部分是通过 WindowManager
来处理的,另一部分是通过 WindowManagerService
来处理的。Window
分为三大类,分别是 Application Window
(应用程序窗口)、Sub Window
(子窗口)和 System Window
(系统窗口),对于不同类型的窗口的添加过程会有所不同,但对于 WindowManagerService
处理部分,添加的过程是一样的。
5.1 Window
的添加过程
5.1.1 WindowManager
部分
系统窗口的添加过程
这里以 StatusBar
为例,StatusBar
是 SystemUI
的重要组成部分,具体就是指系统状态栏,用于显示时间、电量和信号等信息。调用 StatusBar.addStatusBarWindow
方法,将 StatusBar
添加到 Window
中:
// /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
protected StatusBarWindowController mStatusBarWindowController;
protected StatusBarWindowView mStatusBarWindow;
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result); // 1
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight()); // 2
}
注释 1
处用于构建 StatusBar
的视图。注释 2
处调用了 StatusBarWindowController.add(..)
方法,并将 StatusBar
的 视图(StatusBarWindowView
)和 StatusBar
的高度传进去,StatusBarWindowController.add(...)
方法如下所示:
// /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
private final WindowManager mWindowManager;
public void add(ViewGroup statusBarView, int barHeight) {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR, // 1
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp); // 2
mLpChanged.copyFrom(mLp);
onThemeChanged();
}
首先通过创建 LayoutParams
来配置 StatusBar
视图的属性,包括 Width
、Height
、Type
、Flag
、Gravity
、SoftInputMode
等。关键在注释 1
处,设置了 TYPE_STATUS_BAR
,表示 StatusBar
视图的窗口类型是状态栏。在注释 2
处调用了 WindowManager.addView(...)
方法。WindowManager.addView(...)
方法定义在 WindowManager
的父类 ViewManager
中,实现则是在 WindowManagerImpl
中实现的,代码如下所示:
// /frameworks/base/core/java/android/view/WindowManagerImpl.java
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); // ·
}
WindowManagerImpl.addView(...)
方法的第一个参数类型为 View
,说明窗口也是以 View
的形式存储在,在注释 1
处调用了 WindowManagerGlobal.addView(...)
方法,如下所示:
// /frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams); // 1
} 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;
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display); // 2
view.setLayoutParams(wparams);
mViews.add(view); // 3
mRoots.add(root); // 4
mParams.add(wparams); // 5
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView); // 6
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
WindowManagerGlobal
中维护着和 Window
操作相关的 3
个列表,在窗口的添加、更新和删除过程中都会涉及到这 3
个列表,它们分别是 View
列表(ArrayList<View> mViews
)、布局参数列表(ArrayList<WindowManager.LayoutParams> mParams
)和 ViewRootImpl
列表(ArrayList<ViewRootImpl> mRoots
)。
在注释 1
处,如果当前窗口作为子窗口,就会根据父窗口对子窗口的 WindowManager.LayoutParams
类型的 wparams
对象进行调整。在注释 3
处将添加的 View
保存到 View
列表中。在注释 5
处将窗口的参数保存到布局参数列表中。在注释 2
处创建了 ViewRootImpl
并赋值给 root
,紧接着在注释 4
处将 root
存入到 ViewRootImpl
列表中。在注释 6
处将窗口和窗口的参数通过 ViewRootImpl.setView
方法设置到 ViewRootImpl
中,可见,我们添加窗口这一操作是通过 ViewRootImpl
来进行的。ViewRootImpl
身负了很多职责,主要是以下几点:
View
树的根并管理View
树;- 触发
View
的测量、布局和绘制; - 输入事件的中转站;
- 管理
Surface
; - 负责与
WMS
进行进程间通信;
在了解了 ViewRootImpl
的职责后,接着来看 ViewRootImpl.setView(...)
方法:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
final IWindowSession mWindowSession;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mTmpFrame, mAttachInfo.mContentInsets,
mAttachInfo.mStableInsets,
mAttachInfo.mOutsets,
mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets); // 1
setFrame(mTmpFrame);
}
...
}
}
在 ViewRootImpl.addView(...)
方法中调用了 IWindowSession.addToDisplay(...)
方法,mWindowSession
是 IWindowSession
类型的,它是一个 Binder
对象,用于进程间通信,IWindowSession
是 Client
端的代理,它的 Server
端的实现为 Session
。此前的代码逻辑是运行在本地进程的,而 Session.addTodisplay(...)
方法则是运行在 WindowManagerService
(system_server
进程中)。
本地进程的 ViewRootImpl
想要和 WindowManagerService
进行通信需要经过 Session
。下面是 Session.addDisplay(...)
方法,如下所示:
// /frameworks/base/services/core/java/com/android/server/wm/Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout,
InputChannel outInputChannel, InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets,
outDisplayCutout, outInputChannel, outInsetsState);
}
}
在 Session.addToDisplay(...)
方法中调用了 WindowManagerService.addView(...)
方法,并将自身也就是 Session
作为参数传了进去,每个应用程序进程都会对应一个 Session
,WindowManagerService
会用 ArrayList
来保存这些 Session
,剩下的工作就会交给 WindowManagerService
来处理。在 WindowManagerService
中会为这个添加的窗口分配 Surface
,并确定窗口显示次序,可见负责显现界面的是画布 Surface
,而不是窗口本身。WindowManagerService
会将它所管理的 Surface
交由 SurfaceFlinger
处理,SurfaceFlinger
会将这些 Surface
混合并绘制到屏幕上。
PhoneWindow
继承自 Window
,Window
通过 setWindowManager
方法与 WindowManager
发生关联。WindowManager
继承自接口 ViewManager
,WindowManagerImpl
是 WindowManager
接口的实现类,但是具体的功能都会委托给 WindowManagerGlobal
来实现。
Activity
的添加过程
无论是哪种窗口,它的添加过程在 WMS
处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面有些不同。但是在 WindowManager
处理部分会有所不同,以最典型的应用程序窗口 Activity
为例,Activity
在启动过程中,如果 Activity
所在的进程不存在则会创建新的进程,创建新的进程之后就会运行代表主线程的实例 ActivityThread
,ActivityThread
管理着当前应用程序进程的线程,这在 Activity
的启动过程中运用得很明显,当界面要与用户进行交互时,会调用 ActivityThread.handleResumeActivity
方法,如下所示:
// /frameworks/base/core/java/android/app/ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); // 1
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager(); // 2
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l); // 3
} else {
a.onWindowAttributesChanged(l);
}
}
}
...
}
注释 1
处的 performResumeActivity
方法最终会调用 Activity.onResume
方法。在注释 2
处得到 ViewManager
类型的 wm
对象,在注释 3
处调用了 ViewManager.addView
方法,而 addView
方法是在 WindowManagerImpl
中实现的,此后的过程在上面的系统窗口 StatusBar
的添加过程中已经讲述过,唯一需要注意的是 ViewManager.addView
方法的第一个参数是 DecorView
,这说明 Activity
窗口中会包含 DecorView
。
5.1.2 WindowManagerService
部分
无论是系统窗口还是 Activity
,它们的 Window
的添加过程都会调用 WindowManagerService.addWindow(...)
方法。以下会分为三部分来讲解。
Part I:
// /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout,
InputChannel outInputChannel,
InsetsState outInsetsState) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp); // 1
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
synchronized (mGlobalLock) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
// 2
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
...
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { // 3
parentWindow = windowForClientLocked(null, attrs.token, false); // 4
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
...
}
...
return res;
}
WindowManagerService.addWindow(...)
返回的是addWindow(...)
的各种状态,比如添加 Window
成功,无效的 display
等,这些状态被定义在 WindowManagerGlobal
中。在注释 1
处根据 Window
的属性,调用 WindowManagerProlicy.checkAndPermission(...)
方法来检查权限,具体在 PhoneWindowManager.checkAndPermission(...)
方法中实现,如果没有权限则不会执行后续的代码逻辑。在注释 2
处通过 displayId
来获得窗口要添加到哪个 DisplayContent
上,如果没有找到 DisplayContent
,则返回 WindowManagerGlobal.ADD_INVALID_DISPLAY
这一状态,其中,DisplayContent
用来描述一块屏幕。在注释 3
处,type
代表一个窗口的类型,它的数值介于 FIRST_SUB_WINDOW
和 LAST_SUB_WINDOW
之间(1000~1999
),这个数值定义在 WindowManager
中,说明这个窗口是一个子窗口。在注释 4
处,attrs.token
是一个 IBinder
类型的对象,windowForClientLocked(...)
方法内部会根据 attrs.token
作为 key
值从 mWindowMap
中得到该子窗口的父窗口。接着对父窗口进行判断,如果父窗口为 null
或者 type
的取值范围不正确则会返回错误的状态。
说明:WindowManagerPolicy
是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了 WindowManager
所有的特定的 UI
行为。它的具体实现类为 PhoneWindowManager
,这个实现类在 WindowManagerService
创建时被创建。WindowManagerPolicy
允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
Part II:
// /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout,
InputChannel outInputChannel,
InsetsState outInsetsState) {
...
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token); // 1
final int rootType = hasParent ? parentWindow.mAttrs.type : type; // 2
boolean addToastWindowRequiresToken = false;
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
...
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
final boolean isRoundedCornerOverlay =
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow,
isRoundedCornerOverlay); // 3
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { // 4
atoken = token.asAppWindowToken(); // 5
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
} else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
+ " starting window");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
...
}
...
return res;
}
在注释 1
处通过 displayContent.getWindowToken(...)
方法得到 WindowToken
。在注释 2
处,如果有父窗口就将父窗口的 type
值赋值给 rootType
,如果没有将当前窗口的 type
值赋值给 rootType
。接下来如果 WindowToken token == null
,则根据 rootType
或者 type
的进行区分判断,如果 rootType
值等于 TYPE_INPUT_METHOD
、TYPE_WALLPAPER
等值时,则返回状态值 WindowManagerGlobal.ADD_BAD_APP_TOKEN
,说明 rootType
值等于 TYPTE_INPUT_METHOD
、TYPE_WALLPAPER
等值时是不允许 WindowToken
为 null
的。通过多次的条件判断筛选,最后会在注释 3
处隐式创建 WindowToken
,这说明当我们添加窗口时可以不向 WindowManagerService
提供 WindowToken
,前提是 rootType
和 type
的值不为前面条件判断筛选的值。WindowToken
隐式和显式的创建肯定是要加以区分的,注释 3
处的第 4
个参数为 false
就代表这个 WindowToken
是隐式创建的。接下来的代码逻辑就是 WindowToken
不为 null
的情况,根据 rootType
和 type
的值进行判断,比如在注释 4
处判断如果窗口为应用程序窗口,在注释 5
处将 WindowToken
转换为专门针对应用程序窗口的 AppWindowToken
,然后根据 AppWindowToken
的值进行后续的判断。
说明:AppWindowToken
是 WindowToken
的子类,WindowToken
主要有两个作用:
- 可以理解为窗口令牌,当应用程序想要
WindowManagerService
申请创建一个窗口,则需要向WindowManagerService
出示有效的WindowToken
。AppWindowToken
作为WindowToken
的子类,主要用来描述应用程序的WindowToken
结构,应用程序中每个Activity
都对应一个AppWindowToken
; WindowToken
会将同一个组件(比如同一个Activity
)的窗口WindowState
集合在一起,方便管理;
Part III:
// /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout,
InputChannel outInputChannel,
InsetsState outInsetsState) {
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility,
session.mUid,
session.mCanAddInternalSystemWindow); // 1
if (win.mDeathRecipient == null) { // 2
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) { // 3
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
Binder.getCallingUid()); // 4
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = displayPolicy.prepareAddWindowLw(win, attrs); // 5
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
win.attach();
mWindowMap.put(client.asBinder(), win); // 6
win.initAppOpsState();
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
UserHandle.getUserId(win.getOwningUid()));
win.setHiddenWhileSuspended(suspended);
...
boolean imMayMove = true;
win.mToken.addWindow(win); // 7
if (type == TYPE_INPUT_METHOD) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
...
}
...
...
return res;
}
在注释 1
处创建了 WindowState
,它存有窗口的所有的状态信息,在 WindowManagerService
中代表一个窗口。 在创建 WindowState
传入的参数中,this
指的是 WindowManagerService
,client
指的是 IWindow
,IWindow
会将 WindowManagerService
中窗口管理的操作回调给 ViewRootImpl
,token
指的是 WindowToken
。紧接着在注释 2
和注释 3
处分别判断请求添加窗口的客户端是否已经死亡、窗口的 DisplayContent
是否为 null
,如果是则不会再执行下面的代码逻辑。在注释 4
处调用了 WindowManagerPolicy.adjustWindowParamLw(...)
方法,该方法在 PhoneWindowManager
中实现,此方法会根据窗口的 type
对窗口的 LayoutParams
的一些成员变量进行修改。在注释 5
处调用 WindowManagerPolicy.prepareAddWindowLw(...)
方法,用于准备将窗口添加到系统中。在注释 6
处将 WindowState
添加的 mWindowMap
中。在注释 7
处将 WindowState
添加到该 WindowState
对应的 WindowToken
中,实际上是保存在 WindowToken
的父类 WindowContainer
中,这样 WindowToken
就包含了同一组件的 WindowState
。
WindowMangerService.addView(...)
方法总结:
- 对所要添加的窗口进行检查,如果窗口不满足一些条件,就不会再执行下面的代码逻辑;
WindowToken
相关的处理,比如有的窗口类型需要提供WindowToken
,没有提供的话就不会执行下面的代码逻辑,有的窗口类型则需要由WindowManagerService
隐式创建WindowToken
;WindowState
的创建和相关处理,将WindowToken
和WindowState
相关联;- 创建和配置
DisplayContent
,完成窗口添加到系统前的准备工作;
5.2 Window
的更新过程
Window
的更新过程和 Window
的添加过程是类似的,需要调用 ViewManager.updateViewLayout
方法。WindowManager.updateViewLayout
方法在 WindowManagerImpl
中实现,WindowManagerImpl.updateViewLayout
方法中会调用 WindowManagerGlobal.updateViewLayout
方法,如下所示:
// /frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
// /frameworks/base/core/java/android/view/WindowManagerGlobal.java
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); // 1
synchronized (mLock) {
int index = findViewLocked(view, true); // 2
ViewRootImpl root = mRoots.get(index); // 3
mParams.remove(index); // 4
mParams.add(index, wparams); // 5
root.setLayoutParams(wparams, false); // 6
}
}
注释 1
处将更新的参数设置到 View
中,注释 2
处得到要更新的窗口在 View
列表中的索引,注释 3
处在 ViewRootImpl
列表中根据索引得到窗口 ViewRootImpl
,注释 4
处和注释 5
处用于更新布局参数列表,注释 6
处调用 ViewRootImpl.setLayoutParams
方法将更新的参数设置到 ViewRootImpl
中。ViewRootImpl.setLayoutParams
方法在最后会调用 ViewRootImpl.scheduleTraveesals
方法,如下所示:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
...
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 1
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
注释 1
处的 Choreographer
译为“舞蹈指导”,用于接收显示系统的 VSync
信号,在下一个帧渲染时控制执行一些操作。Choreographer.postCallback
方法用于发起添加回调,这个添加的回调将在下一帧被渲染时执行。这个添加的回调指的是注释 1
处的 TraversalRunnable
类型的 mTraversalRunnable
,如下所示:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
在 TraversalRunnable.run()
方法中调用 doTraversal()
方法,如下所示:
// /frameworks/base/core/java/android/view/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;
}
}
}
在 doTraversal
方法中又调用了 performTraversals()
方法,performTraversals()
方法使得 ViewTree
开始 View
的工作流程,如下所示:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
try {
...
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); // 1
...
} catch (RemoteException e) {
}
if (!mStopped) {
...
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 2
}
...
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) {
performLayout(lp, mWidth, mHeight); // 3
,,,
}
...
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw(); // 4
}
...
}
注释 1
处的 relayoutWindow
方法内部会调用 IWindowSession
的 relayout
方法来更新 Window
视图,最终会调用 WMS
的 relayoutWindow
方法。除此之外,performTraversals
方法还会在注释 2
、3
、4
处分别调用 performMeasure
、performLayout
、performDraw
方法,它们内部又会调用 View
的 measure
、layout
和 draw
方法,这就完成了 View
的工作流程。在 performTraversals
方法中更新了 Window
视图,又执行了 Window
中的 View
的工作流程,这样就完成了 Window
的更新。
5.3 Window
的删除过程
和 Window
的创建和更新过程一样,要删除 Window
需要先调用 WindowManagerImpl.removeView(...)
方法,在 WindowManagerImpl.removeView(...)
方法中又会调用 WindowMangerGlobal.removeView(...)
方法,以下是相关代码:
// /frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
// /frameworks/base/core/java/android/view/WindowManagerGlobal.java
@UnsupportedAppUsage
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); // 1
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate); // 2
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
在注释 1
处找到 Window
在 View
列表中的索引,在注释 2
处调用了 removeViewLocked(...)
方法并将这个索引传进去,如下所示:
// /frameworks/base/core/java/android/view/WindowManagerGlobal.java
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index); // 1
View view = root.getView();
if (view != null) {
// 2
InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken()); // 3
}
}
boolean deferred = root.die(immediate); // 4
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
在注释 1
处根据传入的索引在 ViewRootImpl
列表中获得 Window
的 Window
的 ViewRootImpl
。在注释 2
处得到 InputMethodManager
实例,如果 InputMethodManager
实例不为 null
,则在注释 3
处调用 InputMethodManager.windowDismissed(...)
方法来结束 Window
的输入法相关的逻辑。在注释 4
处调用 ViewRootImpl.die(...)
方法,如下所示:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
boolean die(boolean immediate) {
// die 方法需要立即执行并且此时 ViewRootImpl 不再执行 performTraversals 方法
if (immediate && !mIsInTraversal) { // 1
doDie(); // 2
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;
}
在注释 1
处如果 immediate == true
(需要立即执行),并且 mIsInTraversals
为 false
则执行注释 2
处的代码,mIsInTraversal
在执行 ViewRootImpl.performTraversal()
方法时会被设置为 true
,在 ViewRootImpl.performTraverasl()
方法执行完时被设置为 false
,因此,注释 1
处可以理解为 ViewRootImpl.die(...)
方法需要立即执行并且此时 ViewRootImpl
不再执行 performTraversals()
方法。在注释 2
处的 doDie()
方法如下所示:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
void doDie() {
// 1 检查执行 doDie() 方法的线程的额正确性
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) { // 2
return;
}
mRemoved = true; // 3
if (mAdded) { // 4
dispatchDetachedFromWindow(); // 5
}
if (mAdded && !mFirst) { // 6
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
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);
}
} catch (RemoteException e) {
}
}
destroySurface();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this); // 7
}
在注释 1
处用于检查执行 doDie()
方法的线程的正确性,在注释 1
处的内部会判断执行 doDie()
方法线程是否是创建 Window
的原始线程,如果不是就会抛出异常,这是因为只有创建 Window
的原始线程才能够操作 Window
。注释 2
和注释 3
处的代码用于防止 doDie()
方法被重复调用。在注释 4
处 Window
有子 View
就会调用注释 5
处的 dispatchDetachedFromWindow()
方法来销毁 View
。在注释 6
处,如果 Wiindow
有子 View
并且不是第一次被添加,就会执行后面的代码逻辑。在注释 7
处的 WindowManagerGobal.doRemoveView(...)
方法:
// frameworks/base/core/java/android/view/WindowManagerGlobal.java
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root); // 1
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
}
在 WindowManagerGlobal
中维护了和 Window
操作相关的三个列表,doRemoveView(...)
方法会从这三个列表中清除 Window
对应的元素。在注释 1
处找到 Window
对应的 ViewRootImpl
在 ViewRootImpl
列表中的索引,接着根据这个索引从 ViewRootImpl
列表、布局参数列表和 View
列表中删除与 Window
对应的元素。
回到 ViewRootImpl.doDie()
方法中,在注释 5
处的 ViewRootImpl.dispatchDetachedFromWindow()
方法:
// // /frameworks/base/core/java/android/view/ViewRootImpl.java
void dispatchDetachedFromWindow() {
...
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
,,,
}
在 dispatchDetachedFromWindow()
方法中主要调用了 IWindowSession.remove(...)
方法,IWindowSession
在 Server
端的实现为 Session
。Session.remove(...)
方法如下所示:
// /frameworks/base/services/core/java/com/android/server/wm/Session.java
final WindowManagerService mService;
@Override
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
接着查看 WindowManagerService.removeWindow(...)
方法:
// /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
void removeWindow(Session session, IWindow client) {
synchronized (mGlobalLock) {
WindowState win = windowForClientLocked(session, client, false); // 1
if (win == null) {
return;
}
win.removeIfPossible(); // 2
}
}
在注释 1
处用于获取 Window
对应的 WindowState
,WindowState
用于保存窗口的信息,在 WindowManagerService
中 它用来描述一个窗口。接着在注释 2
出调用 WindowState.removeIfPossible()
方法,如下所示:
// /frameworks/base/services/core/java/com/android/server/wm/WindowState.java
@Override
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
...
try {
// 条件判断过滤,满足其中一个条件就会 return,推迟删除操作
...
removeImmediately(); // 1
if (wasVisible) {
final DisplayContent displayContent = getDisplayContent();
if (displayContent.updateOrientationFromAppTokens()) {
displayContent.sendNewConfiguration();
}
}
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
removeIfPossible(...)
方法和它的名字一样,并不是直接执行删除操作的,而是进行多个条件判断过滤,满足其中一个条件就会 return
,推迟删除操作。比如,Window
正在运行一个动画,这时就得推迟删除操作,直到动画完成。通过这些条件判断过滤就会执行注释 1
处的 removeImmediately()
方法:
// // /frameworks/base/services/core/java/com/android/server/wm/WindowState.java
@Override
void removeImmediately() {
super.removeImmediately();
if (mRemoved) { // 1
// Nothing to do.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"WS.removeImmediately: " + this + " Already removed...");
return;
}
mRemoved = true; // 2
...
dc.getDisplayPolicy().removeWindowLw(this); // 3
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
mSession.windowRemovedLocked(); // 4
try {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
} catch (RuntimeException e) {
// Ignore if it has already been removed (usually because
// we are doing this as part of processing a death note.)
}
mWmService.postWindowRemoveCleanupLocked(this); // 5
}
removeImmediately()
方法用于立即进行删除操作。在注释 1
处的 mRemoved
为 true
意味着正在执行删除 Window
操作,在注释 1
到注释 2
之间的代码用于防止重复删除操作。在注释 3
处如果当前要删除的 Window
是 StatusBar
或者 NavigationBar
就会将这个 Window
从对应的控制器中删除。在注释 4
处将 Window
对应的 Session
从 WindowManagerService
的 ArraySet<Session> mSessions
中删除并清除 Session
对应的 SurfaceSession
资源(SurfaceSession
是 SurfaceFlinger
的一个连接,通过这个连接可以创建 1
个或者多个 Surface
渲染到屏幕上)。在注释 5
处调用了 WindowManagerService.postWindowRemoveCleanupLocked(...)
方法用于对 Window
进行一些集中的清理工作。
Window
的删除过程主要有:
- 检查删除线程的正确性,如果不正确就抛出异常;
- 从
ViewRootImpl
列表。布局参数列表和View
列表中删除与Window
对应的元素; - 判断是否可以直接执行删除操作,如果不能就推迟删除操作;
- 执行删除操作、清理和释放与
Window
相关的一些资源;