1 简述
WindowManagerService
是有SystemServer
进程启动的用于管理窗口的服务。
主要包含功能:窗口的添加更新删除、窗口启动、窗口动画、窗口大小和层级等等。
2 WMS启动流程
SystemServer进程启动后,会去启动一系列服务,其中就包括WMS。
/**
* 进程的入口点
*/
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
// 启动 AMS PowerManagerService PackageManagerService 等服务
startBootstrapServices(t);
// 启动了BatteryService UsageStatsService WebViewUpdateService
startCoreServices(t);
// CameraService AlarmManagerService VrManagerService
startOtherServices(t);
...
}
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
t.traceBegin("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
// 创建WMS对象,与AMS关联
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
// 将WMS加入SM,使用的时候去里面拿就行
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与AMS关联
mActivityManagerService.setWindowManager(wm);
t.traceEnd();
t.traceBegin("WindowManagerServiceOnInitReady");
// 初始化监视器等等
wm.onInitReady();
t.traceEnd();
...
}
复制代码
3 核心方法
3.1 addWindow
// 代码过多,就摘取关键性的
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) {
// 检查权限,没有权限不能添加窗口(manifest中注册)
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
WindowState parentWindow = null;
// ...
synchronized (mGlobalLock) {
// 寻找窗口需要添加到哪个displayContent
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
// 避免重复添加
if (mWindowMap.containsKey(client.asBinder())) {
ProtoLog.w(WM_ERROR, "Window %s is already added", client);
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// 判断是否为子窗口,是子窗口就去找父窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
}
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
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;
// 没有token就创建
// 是Toast等类型分别处理
if (token == null) {
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
}
// ...
// 创建WindowState
// 类似ActivityRecord用来保存创建窗口状态
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// 判断添加窗口的客户端是否死亡
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
ProtoLog.w(WM_ERROR, "Adding window client %s"
+ " that is dead, aborting.", client.asBinder());
return WindowManagerGlobal.ADD_APP_EXITING;
}
// displayContent是否为null
if (win.getDisplayContent() == null) {
ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
win.attach();
// windowmap中添加窗口
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
// 将windowstate添加到该windowstate对应的windowtoken中
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
// 窗口动画
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
if (activity != null && activity.isVisible()
&& !prepareWindowReplacementTransition(activity)) {
// If not, check if need to set up a dummy transition during display freeze
// so that the unfreeze wait for the apps to draw. This might be needed if
// the app is relaunching.
prepareNoneTransitionForRelaunching(activity);
}
if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outContentInsets,
outStableInsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
outInsetsState.set(win.getInsetsState(), win.isClientLocal());
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
}
Binder.restoreCallingIdentity(origId);
return res;
}
复制代码
小结addwindow
方法主要是在添加窗口前会进行一系列检查,处理windowToken(有的窗口类型如Toast)需要隐式创建token,完成WindowState和displayContent的处理。
3.2 removeWindow
void removeWindow(Session session, IWindow 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 window
mEmbeddedWindowController.remove(client);
}
}
复制代码
4 Window、WindowManager与WMS
- WMS管理窗口
- Window是一个抽象类,它的实现类是PhoneWindow,主要用来管理view
- WindowManager是一个接口类,实现类WindowManagerImpl,顾名思义,它是用来管理Window的,会与WMS通过binder进行交互
@UnsupportedAppUsage
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
sUseBLASTAdapter = sWindowManagerService.useBLAST();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
复制代码
4.1 addView
WindowManagerImpl -> addView
WindowManagerGlobal -> addView
ViewRootImpl -> setView
// AIDL跨进程调用 Session.java的addToDisplayAsUser方法
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, 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, UserHandle.getUserId(mUid));
}
@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,即调用WMS的addWindow方法
复制代码
4.2 removeView
WindowManagerImpl -> removeView
WindowManagerGlobal -> removeView
WindowManagerGlobal -> removeViewLocked
ViewRootImpl -> die
ViewRootImpl -> doDie
ViewRootImpl -> dispatchDetachedFromWindow
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
@Override
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
复制代码
4.3 updateViewLayout
WindowManagerImpl -> updateViewLayout
WindowManagerGlobal -> updateViewLayout
ViewRootImpl -> setLayoutParams
ViewRootImpl -> scheduleTraversals
ViewRootImpl -> TraversalRunnable
ViewRootImpl -> performTraversals
ViewRootImpl -> relayoutWindow
Seesion -> relayout
WMS -> relayoutWindow
复制代码
setLayoutParams
会对View进行重新布局包括测量、布局、重绘等。- 通过
WindowSession
调用WindowManagerService
的relayoutWindow
更新window窗口。
5 总结
WMS
是在SystemServer进程整体管理系统Window添加 更新 删除
等流程的服务。并且,出于安全的考虑,应用程序App只能通过binder
与WMS进程交互,来管理自身的窗口。
Activity启动过程中,WMS是什么时候添加window窗口的?
在onResume
中。
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// ...
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();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// ...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// WMS添加窗口
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
}
复制代码
为什么dialog的创建不能使用Application context?
dialog是TYPE_APPLICATION类型窗口,要求必需是Activity的Token,不是的话系统会抛出BadTokenException异常。
为什么是Activity的token,主要是为了确保dialog弹出的合法,试下一下,如果是Application Context,那么某些应用就可能在后台弹出一个dialog,让用户误以为是当前App弹出的,是不是很危险?!
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END