前段时间分析了 Window 的添加、更新和删除流程,也知晓了 Activity 、Dialog 和 Toast 中 Window 的创建过程,今天就接着上篇文章,看一下 WMS 的创建 以及WindowManager 添加 WIndow 后 WMS 是怎样进行操作的。上篇文章点这里直达;
WindowManagerService
简称 WMS ,是系统的核心服务,主要分为四大部分,风别是 窗口管理
,窗口动画
,输入系统中转站
,Surface 管理
。
WMS 的职责很多,主要的就是下面这几点:
DisplayContent
,WindowToken
和 WindowState
InputManagerServer(IMS)
会对触摸事件进行处理,他会寻找一个最合适的窗口来处理触摸反馈信息,WMS 是窗口的管理者,因此理所当然的就成为了输入系统的中转站。WMS 的职责可以总结为下图:
WMS 是在 SystemServer
内部启动的
Android 系统在启动的时候,会启动两个重要的进程,一个是 Aygote 进程,两一个是由 Zygote 进程 fork 出来的 system_server 进程,SystemServer 会启动我们在系统中所需要的一系列 Service。
//#SystemServer.java
private void startOtherServices() {
//.....
//1
WindowManagerService wm = null;
InputManagerService inputManager = null;
//2
traceBeginAndSlog("StartInputManagerService");
inputManager = new InputManagerService(context);
traceEnd();
traceBeginAndSlog("StartWindowManagerService");
//WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
//3
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
// WMS 初创建完成后调用,后面会讲到
wm.onInitReady();
//4
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
//5
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
traceEnd();
//...
try {
//6
wm.displayReady();
} catch (Throwable e) {
reportWtf("making display ready", e);
}
traceEnd();
try {
//7
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
traceEnd();
///...
}
startOtherServer 用于启动其他服务,大概有70多个,上面列出了 WMS 和 IMS 的启动逻辑。
在注释1的地方声明了 WMS 和 IMS, 这里值列出了两个,其实非常多。
注释2处创建了 IMS ,
注释 3 调用了 WMS 的 main 方法,并且传入了 IMS,因为 WMS 是 IMS 的中转站。观察 WindowManagerService.main 方法可以知道他是运行在 SystemServer 的 run 方法中,换句话说就是运行在 system_server
线程中。
注释4和5处将 WMS 和 IMS 注册到 ServerManager 里面,这样客户端想要使用 WMS 就需要先去 ServiceManager 中查询信息,然后与 WMS 所在的进程建立通信,这样客户端就可以使用 WMS 了。
注释 6 用来初始化显示信息,注释 7 处用来通知 WMS 系统初始化工作已经完成,内部调用了 WindowManagerPolicy 的 systemReady 方法。
接着,我们看看 WMS 的 maain 方法:
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, Surface::new, SurfaceControl.Builder::new);
}
public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
return sInstance;
}
上面通过 DisplayThread 的 getHandler 方法获取到了 DisplayThread 的 Handler 实例。
DisplayThread 是一个单例的前台线程,用来处理需要低延时显示的相关操作,并且只能由 WindowManager,DisplayManager 和 INputManager 试试执行快速操作。
runWithScissors
表达式中创建了 WMS 对象。至于 Handler.runWithScissors 方法,**有兴趣的可以看一下这篇文章**。
到这里我们就知道 WMS 是从何处启动的了,下面我们来看一下 WMS 的构造方法
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
...
//1
mInputManager = inputManager; // Must be before createDisplayContentLocked.
//2
mPolicy = policy;
//3
mAnimator = new WindowAnimator(this);
//4
mRoot = new RootWindowContainer(this);
//5
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
//6
mActivityManager = ActivityManager.getService();
//7
LocalServices.addService(WindowManagerInternal.class, new LocalService());
...
}
在上面 WMS的启动中 WMS 创建完成后会调用 wm.onInitReady
方法,下面我们来看一下这个方法:
public void onInitReady() {
initPolicy();
//将 WMS 添加到 Watchdog 中,Watchdog 用来监控一下系统关键服务的运行情况。
//这些被监控的服务都会实现 Watchdog.Monitor 接口,Watchdog 每分钟都会对
//被监控的服务进行检测,如果被监控的服务出现了死锁,则会杀死 Watchdog 所在的进程
Watchdog.getInstance().addMonitor(this);
......
}
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);
}
}, 0);
}
上面的和 WMS 的 main 方法类似,WindowManagerPolicy (简称 WMP) 是一个接口,init 的具体实现在 PhoneWindowManager(PWM) 中,并且通过上面代码我们知道,init 方法运行在 android.ui
线程中。因此他的线程优先级要高于 android.display
线程,必须等 init 方法执行完成后,android.display
线程才会被唤醒从而继续执行下面的代码。
WindowManagerPolicy 用来定义一个窗口策略所要遵循的通用规范,,并提供了 WindowManager 所有的特定 UI 行为 他的具体实现类为 PhoneWindowManager
在上面的文章中,一共提供了三个线程,分别是 system_server
,android.display
,android.ui
,他们之间的关系如下图所示:
system_server
线程中会调用 main 方法,mian 方法中会创建 WMS,创建的过程实在 android.display
线程中,他的优先级会高一些,创建完成后才会唤醒处于 system_server
线程。
WMS 创建完成后会调用 onInitReady 中的 initPolicy 方法,该方法中会调用 PWM 的 init() 方法,init 方法调用完成之后就会唤醒 system_server
线程。init 方法运行中 android.ui
线程中。
之后就会接着执行 system_server
中的代码,例如 displayReady 等。
Window 的操作有两大部分,一部分是 WindowManager 来处理,一部分是 WMS 来处理,如下图所示:
我们都知道 Window 的添加最后是通过 ViewRootImpl.addTodisplay 方法来完成的,我们先来看一下:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
....
try {
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
}
...
}
}
这里调用了 mWindowSession.addToDisplayAsUser 来完成最后的添加,我们先看看 mWindowSession 是个啥东西。
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
//WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
....
IWindowManager windowManager = getWindowManagerService();
//通过 windowManager 创建一个 IWindowSession
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
}
return sWindowSession;
}
}
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
// 通过 AIDL 获取 IWindowManager
sWindowManagerService = IWindowManager.Stub.asInterface(
//获取 WindowManagerService 的 IBinder
ServiceManager.getService("window"));
//...
}
return sWindowManagerService;
}
}
// WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);//传入了 this,将 Session 就会持有 WindowManagerService 的引用
}
mWindowSession 是 IwindowSession 的对象,实现类是 Session。
上面代码中,在 VIewRootImpl 初始化的时候,通过IPC 获取到 IWindowManager,然后通过 IPC 的调用创建了 Session 对象。
接着我们来看一下 mWindowSession.addToDisplayAsUser
方法,也就是在 Session 类中
@Override
public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls, userId);
}
可以看到,最终是通过 mService 来完成添加的,
需要注意的是,WMS 并不关系 View 的具体内容,他只关心各个应用显示的界面大小,层级值等,这些数据到包含在 WindowManager.LayoutParams 中。也就是上面的 atrs 属性。
注意 addWindow
的第二个参数是一个 IWindow 类型,这是 App 暴露给 WMS 的抽象实例,在 ViewRootImp 中实例化,与 ViewRootImpl 一一对应,同事也是 WMS 向 App 端发送消息的 Binder 通道。
从 WindowManager 到 WMS 的具体流程如下所示:
final WindowManagerPolicy mPolicy;
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final AppOpsManager mAppOps;
final DisplaySettings mDisplaySettings;
...
final ArraySet<Session> mSessions = new ArraySet<>();
final WindowHashMap mWindowMap = new WindowHashMap();
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
WindowState[] mPendingRemoveTmp = new WindowState[20];
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
...
final H mH = new H();
...
final WindowAnimator mAnimator;
...
final InputManagerService mInputManager
1. 可以理解为窗口令牌,当应用程序想要向 WMS 申请创建一个窗口,则需要向 WMS 出示有效的 WindowToken。AppWindowToken 作为 WindowToken 的子类,主要用来描述应用程序的 WindowToken 结构。
2. WindowToken 会将相同组件(例如 Activity)的窗口(WindowState)集合在一起,方便管理。
mFinishedStarting 就是用于存储已经完成启动的应用窗口程序窗口(比如Activity) 的 AppWindowToken 的列表。
除了 mFinishedStarting,还有类似的 mFinishedEarlyAnim和 mWindowReplacementTimeouts
其中 mFinishedEarlyAnim 存储了已经完成窗口绘制并且不需要再展示任何已经保存 surface 的应用程序窗口的 AppWindowToken
mWindowReplacementTimeout 存储了等待更换的应用程序窗口的 AppWindowToken,如果更换不及时,旧窗口就需要被处理
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
int[] appOp = new int[1];
//1
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
synchronized (mGlobalLock) {
//2
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
...
//3
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;
}
}
...
}
WMS 的 addWindow
方法返回的是 addWindow
的各种状态,例如 添加成功,失败,无效的 display 等,这些状态定义在 WindowManagerGloabl 中 。
注释1 处调用了 checkAddPermission 方法来检查权限,mPolicy
的实现类是 PhoneWindowManager
。
注释2 通过 displayId 来获得 Window 要添加到那个 DisplayContent,如果没有找到,则返回 WindowManagerGlobal.ADD_INVALID_DISPLAY
状态。其中DisplayContent 用来描述一块屏幕。
如下面代码,从 mRoot(RootWindowContainer) 对应的 DisplayContent,如果没有,则创建一个再返回,RootWindowContainer 是用来管理 DisplayContent 的。
private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
if (token != null) {
final WindowToken wToken = mRoot.getWindowToken(token);
if (wToken != null) {
return wToken.getDisplayContent();
}
}
DisplayContent displayContent = mRoot.getDisplayContent(displayId);
// Create an instance if possible instead of waiting for the ActivityManagerService to drive
// the creation.
if (displayContent == null) {
final Display display = mDisplayManager.getDisplay(displayId);
if (display != null) {
displayContent = mRoot.createDisplayContent(display, null /* controller */);
}
}
return displayContent;
}
注释3 判断 type 的窗口类型(100 - 1999),如果是子类型,必须要有父窗口,并且父窗口不能是子窗口类型
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
//1
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
//2
if (token == null) {
...
//3
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
//4
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { //5
atoken = token.asAppWindowToken();
if (atoken == null) {
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
}
....
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
.....
注释 1 处通过 displayContent 的 getWindowToken 方法得到父窗口的 WindowToken 或者是当前窗口的 WindowToken。RootType 也同样如此。
注释2处如果 token 等于 null,并且不是应用窗口或者是其他类型的窗口,则窗口就是系统类型的了(例如 Toast),就进行隐式创建 WindowToken,这说明我们添加窗口时是可以不向 WMS 提供 WindowToken 的,WindowToken 的隐式和显式创建是需要区分的,第四个参数 false 表示隐式创建。一般系统窗口都不需要添加 token,WMS 会隐式创建。例如 Toast 类型的床
接着就是 token 不为空的情况,会在注释 4 处判断是否位 应用窗口
,如果是 应用窗口,就会讲 WindowToken 转换为针对于应用程序窗口的 AppWindowToken,然后再继续进行判断。
//1
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
//2
if (win.mDeathRecipient == null) {
return WindowManagerGlobal.ADD_APP_EXITING;
}
//3
if (win.getDisplayContent() == null) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
//4
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
Binder.getCallingUid());
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
//5
res = displayPolicy.prepareAddWindowLw(win, attrs);
....
//6
mWindowMap.put(client.asBinder(), win);
boolean imMayMove = true;
//7,添加窗口
win.mToken.addWindow(win);
...
在注释1 处创建了WindowState
,保存了窗口的所有状态信息(例如 WMS ,Session,WindowToken等),在 WMS 中它代表一个窗口。WindowState 与窗口时一一对应的关系
在注释2和注释3处判断请求添加窗口的客户端是否已经死亡,如果死亡则不会执行下面逻辑。
注释 4处调用了 adjustWindowParamsLw 方法,这里会根据窗口的 type 类型对窗口的 LayoutParams 的一些成员变量进行修改。源码注释信息为 清理来自客户端的布局参数。允许策略 做一些事情,比如确保特定类型的窗口不能 输入焦点
注释 5处调用了 prepareAddWindowLw
方法用于准备将窗口添加到系统中
注释 6处将 WindowState 添加到 mWindowMap 中,mWindowMap 是各种窗口的集合。
注释 7 处将 WindowState 添加到该 WindowState 对应的 WindowToken 中(实际上就是保存在 WindowToken 的父类 WindowContainer),这样 WindowToken 就包含了相同组件的 WindowState。
1. 可以理解为窗口令牌,当应用程序想要向 WMS 申请创建一个窗口,则需要向 WMS 出示有效的 WindowToken。并且窗口类型必须与所持有的 WindowToken 的类型一致。 从上面的代码中可以看到,在创建系统类型窗口时不需要提供有效的 Token,WMS 会隐式的创建一个 WindowToken,看起来谁都可以添加这个系统窗口,但是在 addWindow 方法一开始就调用
2. WindowToken 会将相同组件(例如 Activity)的窗口(WindowState)集合在一起,方便管理。 至于为什么说会集合在一起,因为有些窗口时复用的同一个 token,例如 Activity 和 Dialog 就是复用的同一个 AppToken,Activity 中的 PopWindow 复用的是一个 IWindow 类型 Token,Toast 系统类型的窗口也可以看成 null,就算不是 null,WMS 也会强制创建一个隐式 token。
通过上面的流程,App 到 WMS 注册窗口的流程就完了,WMS 为窗口创建了用来描述状态的 WindowState,接下来就会为新建的窗口显示次序,然后再去申请 Surface,才算是真正的分配了窗口。接下来还有一部分,后面再慢慢搞吧。
这里对 WMS 的 addWindow 流程做一个总结 :
displayId
创建一个新的。DisplayContentwin.mToken.addWindow(win)
,这里的 mToken 就是上面 第三步 获取的 token,然后将 WindowState 添加到 WindowToken 中。因为 WindowToken 是可以复用的,所以这里的关系就是,每个 WindowToken 都会保存对应的 WindowState,而每个 WindowState 也都会都持有 WindowToekn。WindowToken
、WindowState
、RootWindowContainer
、DisplayContent
都是继承自 WindowContainer,至于这里为啥这么写,我也不太懂,慢慢再看呗。到这里这篇文章也写完了,但是 WMS 的窗口管理 还有一部分没写,原因就是因为我也还没看懂,所以就先写到这里,等后面看懂了,学会了再写一篇。
文章中写的也不一定全部是正确的,我本人也是自己学自己写,然后再慢慢的梳理,如果那些错了,或者是有任何问题可在下方评论,或者是直接私信我。