WindowManager
- 我们知道Window是通过WindowManager来管理的,而与WindowManager对应的还有一个WMS(WindowManagerService)那么他们两者到底是怎么工作的呢?接下来我们就来具体分许一下
- 这篇文章我们重点分析WindowManager的基本知识脉络,而不去看具体的实现细节,我们只关心WindowManager和WindowManagerService(以下简称WMS),Surface,SurfaceFinger之间如何建立关联,以及交互的过程
- Android系统中有很多个服务,这些服务在App执行的时候会将所用到的服务全都注册到一个Map当中(ContextImpl的register方法),当我们想使用某个服务的时候,就可以通过Context.getSystemService来获取服务
- 那么WindowManager也是一种服务,在SystemServiceRegistry的静态区里面会对系统所用的一些服务进行注册,而WindowManager就是其中一种,我们来看看注册的具体操作
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
- 其中,前者我们应该挺熟悉,当我们想要得到某个服务的时候会这样调用
getSystemService(Context.WINDOW_SERVICE);
- 所以,注册方法的内部实现其实是类似于Map,两个参数其实就是一个键值对,我们可以很容易通过Key得到Value
- 好了,我们看到WindowManager服务其实是WindowManagerImpl的对象,我们去看一下这个类的实现
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Context context) {
this(context, null);
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
return new WindowManagerImpl(displayContext, mParentWindow);
}
/**
* Sets the window token to assign when none is specified by the client or
* available from the parent window.
*
* @param token The default token to assign.
*/
public void setDefaultToken(IBinder token) {
mDefaultToken = token;
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
if (mDefaultToken != null && mParentWindow == null) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (wparams.token == null) {
wparams.token = mDefaultToken;
}
}
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
@Override
public void requestAppKeyboardShortcuts(
final KeyboardShortcutsReceiver receiver, int deviceId) {
IResultReceiver resultReceiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
List result =
resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
receiver.onKeyboardShortcutsReceived(result);
}
};
try {
WindowManagerGlobal.getWindowManagerService()
.requestAppKeyboardShortcuts(resultReceiver, deviceId);
} catch (RemoteException e) {
}
}
@Override
public Display getDefaultDisplay() {
return mContext.getDisplay();
}
}
- 这个类并不长,我们可以很轻易的分析到这个类就是WindowManager操作Window的类,不过具体的实现细节还是由WindowManagerGlobal这个类来做的,这个类是一个单例模式
- 那么管理Window的类找到了,他和Window之间的关系呢?我们在这里来看Dialog的实现吧,因为Dialog其实也是一个Window,并且它支持自定义布局,所以肯定会用到WIndowManager来管理Window,而且他的内容少,分析起来不是那么费劲
- 我们先去看看Dialog的构造方法
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == ResourceId.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
w.setWindowManager(mWindowManager, null, null);
- 可见,我们在他的构造方法里面得到操作Window的WIndowManager,然后将WindowManager设置给Window,那个getSystemService方法就不看了,就是从前面所说的Map当中获取到那个WindowManagerImpl类实例,然后接下来我们看看setWindowManager这个方法,看看一个Window是如何和WindowManager建立联系,从而能让前者被后者管理,这个setWindowManager是Window的方法,我们点进去看看
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
setWindowManager(wm, appToken, appName, false);
}
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);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
- 所以,我们去看看WindowManagerImpl的createLocalWindowManager方法
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
- 这里只不过是通过之前传进来的WindowManagerImpl实例又去创建了一个具有ParentWindow的WindowManagerImpl实例,根据他的参数可以看出,这里的Dialog本身的Window是作为WindowManagerImpl的parentWindow的,也就是他们俩的关系从表面看上去好像是父级和子级的层级关系
- 那么关于这个parentWindow参数的意义,我们暂时先不管,带着这个问题继续看
- 根据我们的推论,和对上面WindowManagerImpl这个类的代码的简单分析,我们不难得出,WindowManager对Window的操作主要来自于三个方面( addView,updateViewLayout,removeView),而我们也看到了,这三种方法的实现都是通过WindowManagerGlobal这个类来实现的,而这个类在调用addView的时候是传进了parentWindow这个参数,我们就去看看WindowManagerGlobal这个类对这三种方法的具体实现吧
- 先看addView方法
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);
} else {
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) {
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
- 在上面方法的最后我们看到最终是通过root(ViewRootImpl类实例)的setView来显示到界面上面的,而ViewRootImpl并不是一个View,他是native层和FrameWork层通信的桥梁,比如我们熟悉的performTraversals函数收到系统绘制View的消息之后,通过调用视图树的各个节点的measure,layout,和draw方法来绘制整颗视图树
- 而WindowManager是FrameWork层的,但是WMS运行在Native层的,而这两者之间的通信就是通过ViewRootImpl来做的,我们具体看看他是怎么做的
- 在上个方法中先是构造出一个ViewRootImpl实例,所以我们先去看看他的构造方法
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
}
- 其他东西我删掉了,在这里我们看到先是获取WindowSession,然后再得到了一个线程的引用,这个线程其实就是UI线程。
- 这里的获取Session也就是与WMS建立连接,我们点进去接着看
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;
}
}
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;
}
}
- 在getWindowSession方法中,frameWork层先通过getWindowManagerService方法获取到IWindowManager对象,在getWindowManagerService方法发中,通过ServiceManager.getService(“window”)获取到WMS,点到ServiceManager.getService方法中
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(getIServiceManager().getService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
- 可见该方法返回的是一个IBinder对象,然后通过IWindowManager.Stub.asInterface将他包装成一个相应进程的通信的对象(这里我也不知道怎么解释这个的好,不明白Binder机制的可以去看看我的这篇文章,Android-IPC机制探索 )
- 最后通过 windowManager.openSession与WMS方法建立一个通信回话,相当于FrameWork层与Native层建立了一个长期合作的办事处,双方有什么需求都可以通过Session来交换信息,
- 但是,,这个时候Dialog(或者说是Activity的View)并不能显示在手机屏幕上,WMS只是负责管理手机屏幕上的View的Z-Order(也就是View的显示层次,最上面的View层次当然能被人们所见,最下面的被上面的View覆盖,当然是不可见状态)
- 这里看明白之后我们再回到前面的addView,他有一个ParentWindow参数,在前面的分析中,我们可以分析出,WMS管理的并非是Window,而是Window里面的View,这个不难理解
- 然后我们接着看addView里面的ViewRootImpl的setView方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
- 这个方法很长,我将大部分东西都删掉,这里我们只关注这两个方法,先 requestLayout();然后在mWindowSession.addToDisplay,我们先看看第一个方法
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
- 我点到mHandler.getLooper().getQueue().postSyncBarrier()这个方法里面看了下,并没有找到我们想要的处理Traversal的东西,不过看下面这句代码mChoreographer.postCallback貌似有点名堂,先看看他的这个postCallback方法
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
- 最终会生成一个what值为MSG_DO_SCHEDULE_CALLBACK的Message,点到这个Handler的handleMessage方法里面找到这个参数的部分
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
- 执行的是doScheduleCallback(msg.arg1);这个方法,我们回过头再看看刚才给Msg设置的那个action值
- 他在一开始是在这里传入的
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)
- 这里的mTraversalRunnable也就是我们那会传的action,看看这个mTraversalRunnable的实现
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
- 最终调用了performTraversals();,而这个方法的内容主要有四步:1. 获取Surface对象,用于图形的绘制,2. 丈量整个视图树的各个View的尺寸
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
- 3.布局整个视图树,performLayout(lp, mWidth, mHeight);, 4.绘制整个视图树 performDraw();
- 在第四步中,Framework会获取到图形绘制表面Surface对象,然后获取他的可绘制区域,也就是我们Canvas对象,然后开始绘制
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
- 然后调用 draw(fullRedrawNeeded);
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return;
}
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
mHardwareYOffset = yOffset;
mHardwareXOffset = xOffset;
invalidateRoot = true;
}
if (invalidateRoot) {
mAttachInfo.mThreadedRenderer.invalidateRoot();
}
dirty.setEmpty();
final boolean updated = updateContentDrawBounds();
if (mReportNextDraw) {
mAttachInfo.mThreadedRenderer.setStopped(false);
}
if (updated) {
requestDrawWindow();
}
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
if (mAttachInfo.mThreadedRenderer != null &&
!mAttachInfo.mThreadedRenderer.isEnabled() &&
mAttachInfo.mThreadedRenderer.isRequested()) {
try {
mAttachInfo.mThreadedRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return;
}
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
- 在draw方法中会获取到需要绘制的区域,一般我们使用的是CPU绘制,也就是drawSoftware方法
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
canvas.setDensity(mDensity);
}
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true;
return false;
}
if (LOCAL_LOGV) {
Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
}
}
return true;
}
- 整个流程在代码中已经注释清楚,在绘制完成之后请求WMS显示该窗口的内容
- 最后来张图