https://www.jianshu.com/p/e4b19fc36a0e
简介
在学习WindowManagerService窗口管理服务之前,我们需要先了解WMS中的几个重要概念:
- 1.Window窗口
- 2.Session
- 3.WindowState窗口
- 4.WindowToken窗口
- 5.DisplayContent窗口
以及WMS与App直接通过两个ALDI实现相互调用;
后面,我们会根据功能流程进行分析,Window的添加、移除、Z-Order排列
等,会分为两个章节分析;
一、初步了解概念
1.1 Session理解
Session是ViewRootImpl和WMS之间的连接,Session集成IWindowSession.Stub
,每一个应用进程都有一个唯一的Session对象与WMS通信,如图所示:
在 WMS 中的
mSessions
成员属性,是 ArraySet 类型,其中保存着 Session 型的对象。Session获取路径ViewRootImpl-->WindowManagerGlobal-->IWindowManager-->WindowManagerService
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
/**
* All currently active sessions with clients.
*/
final ArraySet<Session> mSessions = new ArraySet<>();
......
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
}
public class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
final WindowManagerService mService;
private int mNumWindow = 0; // 代码 1
......
// 代码 2
public Session(WindowManagerService service, IWindowSessionCallback callback,
IInputMethodClient client, IInputContext inputContext) {
mService = service;
......
}
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
TAG_WM, "First window added to " + this + ", creating SurfaceSession");
mSurfaceSession = new SurfaceSession();
if (SHOW_TRANSACTIONS) Slog.i(
TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession);
// 代码 3
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
mNumWindow++;
}
void windowRemovedLocked() {
mNumWindow--;
killSessionLocked();
}
private void killSessionLocked() {
if (mNumWindow > 0 || !mClientDead) {
return;
}
// 代码 4
mService.mSessions.remove(this);
......
}
}
- 代码 1 处,
mNumWindow
变量记录着此 Session 中共有多少个Window
- 代码 2 处的
Session
的构造方法中,mService
保存着 WMS 的实例对象 - 代码 3 处,将此
Session
对象添加进 WMS 的mSessions
队列中 - 代码 4 处,将此
Session
对象从 WMS 的mSessions
队列中移除
1.2 WindowState理解
WindowState 是 WMS 中一个重要的概念,在 WMS 中的一个 WindowState 对象就对应着一个应用进程中的 Window 对象。
通过初步理解Window体系我们了解到,
在调用WindowManagerGlobal.addView
方法时,经过一系列的方法调用,最后走到了 WindowManagerService.addWindow
方法中
在
WindowManagerService.addWindow
方法中,会创建一个与Window
对象对应的WindowState
对象并调用 WindowState.attach
方法,然后将该 WindowState
对象添加到 WMS 的 mWindowMap Map
中
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
final WindowHashMap mWindowMap = new WindowHashMap();
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
......
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
......
win.attach();
mWindowMap.put(client.asBinder(), win);
......
win.mToken.addWindow(win);
}
}
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
final WindowManagerService mService;
final WindowManagerPolicy mPolicy;
final Context mContext;
final Session mSession;
final IWindow mClient;
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
mService = service;
mSession = s;
mClient = c;
mAppOp = appOp;
mToken = token;
mAppToken = mToken.asAppWindowToken();
......
}
void attach() {
if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked(mAttrs.packageName);
}
......
}
在 WindowState 中保存了 WMS 对象、WMP 对象、Session 对象和 IWindow 对象
,IWindow 对象就是与此 WindowState 对象相对应的在应用进程中的 Window 对象
。
final WindowHashMap mWindowMap = new WindowHashMap();
是一个 HashMap 的子类,key 是 IBinder
,value 是 WindowState
,用于保存 WMS 中所有的 WindowState 对象,
/**
* Subclass of HashMap such that we can instruct the compiler to boost our thread priority when
* locking this class. See makefile.
*/
class WindowHashMap extends HashMap<IBinder, WindowState> {
}
mWindowMap.put(client.asBinder(), win);
IWindow client
对象,其实是 ViewRootImpl
中的 final W mWindow
成员,如下所示:
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
final W mWindow;
......
static class W extends IWindow.Stub {
......
}
......
}
1.3 WindowToken理解
简单的理解,WindowToken 有两个作用:
-
1.在 WMS 中,
一个 WindowToken 就代表着一个应用组件,应用组件包括:Activity、InputMethod 等
。在 WMS 中,会将属于同一 WindowToken 的做统一处理,比如在对窗口进行 ZOrder 排序时,会将属于统一 WindowToken 的排在一起。 - 2.WindowToken 也具有令牌的作用。应用组件在创建 Window 时都需要提供一个有效的 WindowToken 以表明自己的身份,并且窗口的类型必须与所持有的 WindowToken 类型保持一致。如果是系统类型的窗口,可以不用提供 WindowToken,WMS 会自动为该系统窗口隐式的创建 WindowToken,但是要求应用必须具有创建该系统类型窗口的权限
概念上有了初步的理解,我们来看下 WindowToken 的代码,如下所示,在 WindowToken 类中,最重要的其实是其中的成员属性
/**
* Container of a set of related windows in the window manager. Often this is an AppWindowToken,
* which is the handle for an Activity that it uses to display windows. For nested windows, there is
* a WindowToken created for the parent window to manage its children.
*/
class WindowToken extends WindowContainer<WindowState> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
// The window manager!
protected final WindowManagerService mService;
// The actual token.
final IBinder token;
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
......
// The display this token is on.
protected DisplayContent mDisplayContent;
......
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens) {
mService = service;
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
onDisplayChanged(dc);
}
void onDisplayChanged(DisplayContent dc) {
dc.reParentWindowToken(this);
mDisplayContent = dc;
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
// to another display before the window behind
// it is ready.
SurfaceControl.openTransaction();
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState win = mChildren.get(i);
win.mWinAnimator.updateLayerStackInTransaction();
}
SurfaceControl.closeTransaction();
super.onDisplayChanged(dc);
}
......
}
其实,对于 WMS 来讲,只要是一个 IBinder 对象都可以作为 Token,比如在之前分析添加 Window 时,调用 WindowManagerService.addWindow
方法时,传入的 Token 对象就是一个IWindow.Stub
的对象。我们来看一下 WMS 中的addWindowToken
方法,如下所示:
@Override
public void addWindowToken(IBinder binder, int type, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
// 代码 1
WindowToken token = dc.getWindowToken(binder);
// 代码 2
if (token != null) {
Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
+ " for already created window token: " + token
+ " displayId=" + displayId);
return;
}
// 代码 3
if (type == TYPE_WALLPAPER) {
new WallpaperWindowToken(this, binder, true, dc,
true /* ownerCanManageAppTokens */);
} else {
new WindowToken(this, binder, type, true, dc, true /* ownerCanManageAppTokens */);
}
}
}
- 代码 1 处,从 DisplayContent 中取一个 WindowToken 对象。从这儿可以看出每一个 WindowToken 又具体是属于每个 DisplayContent 对象的,DisplayContent 对象可以理解为一块屏幕的对应,这个概念在之后详细介绍。
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
WindowManagerService mService;
// mTokenMap 一个 HashMap 对象,用于映射 IBinder 和 WindowToken 对象
// Mapping from a token IBinder to a WindowToken object on this display.
private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
......
// 从 mTokenMap 取出对应于 IBinder 的 WindowToken 对象
WindowToken getWindowToken(IBinder binder) {
return mTokenMap.get(binder);
}
// 在创建 WindowToken 对象时,会通过此方法将 WindowToken 从属于此 DisplayContent 对象,并添加到 mTokenMap 中
/** Changes the display the input window token is housed on to this one. */
void reParentWindowToken(WindowToken token) {
final DisplayContent prevDc = token.getDisplayContent();
if (prevDc == this) {
return;
}
if (prevDc != null && prevDc.mTokenMap.remove(token.token) != null
&& token.asAppWindowToken() == null) {
// Removed the token from the map, but made sure it's not an app token before removing
// from parent.
token.getParent().removeChild(token);
}
addWindowToken(token.token, token);
}
private void addWindowToken(IBinder binder, WindowToken token) {
final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
// this moving forward we will either need to merge the WindowTokens some how or have
// the binder map to a list of window tokens.
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
}
if (binder == null) {
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " binder is null");
}
if (token == null) {
throw new IllegalArgumentException("Can't map null token to display="
+ getName() + " binder=" + binder);
}
mTokenMap.put(binder, token);
if (token.asAppWindowToken() == null) {
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
switch (token.windowType) {
case TYPE_WALLPAPER:
mBelowAppWindowsContainers.addChild(token);
break;
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainers.addChild(token);
break;
default:
mAboveAppWindowsContainers.addChild(token);
break;
}
}
}
// 通过此方法,将 IBinder 所对应的 WindowToken 对象从此 DisplayContent 中的 mTokenMap 移除
WindowToken removeWindowToken(IBinder binder) {
final WindowToken token = mTokenMap.remove(binder);
if (token != null && token.asAppWindowToken() == null) {
token.setExiting();
}
return token;
}
......
}
- 代码 2 处,若该 IBinder 对象对应的 WindowToken 不为空,则返回,可见 一个 IBinder 对象只能创建一个对应的 WindowToken 对象。
- 代码 3 处,根据 Window 的 type 类型创建对应的 WindowToken 对象。
AppWindowToken
是WindowToken
的子类,与WindowToken
不同的是,AppWindowToken
只可以用于Activity
中的Window
的WindowToken
。
1.4 DisplayContent理解
DisplayContent 是 Android 4.2 中支持 WiFi Display 多屏幕显示提出的一个概念,一个 DisplayContent 对象就代表着一块屏幕信息,一个 DisplayContent 对象用一个整型变量作为其 ID,系统默认屏幕所对应的 DisplayContent 对象 ID 是 Display.DEFAULT_DISPLAY。
属于同一个 DisplayContent 对象的 Window 对象会被绘制到同一块屏幕上,在添加窗口时可以指定对应的 DisplayContent 的 id,从而指定被添加到哪个 DisplayContent 上面。
DisplayContent 对象是由 DisplayManagerService 统一管理的,在此只做概念性的介绍,详细的关于 DisplayContent 和 DisplayManagerService 知识请看第五章
二. WMS 与 WindowManager 的通信
通过初步理解Window体系中,我们最后分析到了ViewRootImpl,ViewRootRImpl是连接WindowManager和WMS的桥梁
;自然他们之间的通讯也是通过ViewRootImpl完成;
重点:
内部通过双向Bindler实现双向通信调用
2.1 ViewRootImpl 的成员变量
在 ViewRootImpl 中有两个个非常重要的成员变量:mWindowSession 和 mWindow,这两个变量都是用于 ViewRootImpl 和 WMS 通信使用的;
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
......
final IWindowSession mWindowSession;
final W mWindow;
......
public ViewRootImpl(Context context, Display display) {
mContext = context;
// 代码 1,通过 WindowManagerGlobal.getWindowSession() 方法得到一个 IWindowSession 对象
mWindowSession = WindowManagerGlobal.getWindowSession();
......
// 代码 2,通过 W 构造方法直接创建一个新的 W 对象
mWindow = new W(this);
......
}
......
}
2.2 IWindowSession 通信接口 WMS提供服务
IWindowSession 是一个 AIDL 接口,其服务端进程是 WMS,客户端进程是应用进程
,IWindowSession 的创建是在 WindowManagerGlobal 中,如下所示:
public final class WindowManagerGlobal {
private static IWindowManager sWindowManagerService;
private static IWindowSession sWindowSession;
......
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());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
......
}
- 从
getWindowSession()
方法中我们可以看出,IWindowSession 对象的创建依赖于 IWindowManager 对象 - IWindowManager 也是一个 AIDL 接口,通过
getWindowManagerService()
方法得到其对象,在getWindowManagerService()
方法中,可以看到是典型的 Android 中 Binder 通信得到服务端在客户端进程中的代理对象的方式,远程端的对象即是 WMS,WMS 实现了 IWindowManager 接口
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
......
}
- 在
getWindowSession()
方法中,我们可以看到是调用了 IWindowManager 的openSession
方法,其实际的实现是在 WMS 中,WMS
中的openSession
方法如下所示
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
}
image.png
可以看到,其实 ViewRootImpl 中的 IWindowSession 对象实际对应着 WMS 中的 Session 对象。
WindowManagerGlobal 和 WMS 实现的是单方向的通信,都是通过如下图所示的 Binder 方式进行进程间通信的
2.2 W通信接口 App提供服务
W 类是 ViewRootImpl 的一个内部类,实现了 IWindow 接口,IWindow 也是一个 AIDL 接口,可以猜想到,IWindow 接口是供 WMS 使用的,WSM 通过调用 IWindow 一些方法,通过 Binder 通信的方式,最后执行到了 W 中对应的方法中
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
}
}
@Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchMoved(newX, newY);
}
}
@Override
public void dispatchAppVisibility(boolean visible) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchAppVisibility(visible);
}
}
@Override
public void dispatchGetNewSurface() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchGetNewSurface();
}
}
@Override
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
private static int checkCallingPermission(String permission) {
try {
return ActivityManager.getService().checkPermission(
permission, Binder.getCallingPid(), Binder.getCallingUid());
} catch (RemoteException e) {
return PackageManager.PERMISSION_DENIED;
}
}
......
}
}
比如在ViewRootImpl#setView
方法中,有如下代码,在代码 1 处通过 mWindowSession 调用 addToDisplay 方法时,会将 mWindow 传入,最后传给 WMS,这样 WMS 便得到了一个 W 对象的实例对象
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
......
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// 代码 1
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
......
}
}
}
}
从上面代码可以看到,在 ViewRootImpl 中不仅实现了从 ViewRootImpl 向 WMS 的通信,也实现了从 WMS 向 ViewRootImpl 的通信,如下图所示
image.png
网友评论