6.4窗口管理服务WMS
目录介绍
- 01.Window窗口介绍
- 1.1 Android窗口管理概念
- 1.2 WindowManager介绍
- 02.Window的添加流程
- 03.Window的删除流程
- 04.Window的更新流程
01.Window窗口介绍
1.1 Android窗口管理概念
- Android窗口管理从窗口的创建到UI的绘制主要涉及以下角色:
- WindowManager:应用与窗口管理服务WindowManagerService交互的接口
- WindowManagerService:窗口管理服务,继承于IWindowManager.Stub,是Binder的服务端,该服务运行在一个单独的进程中,因此WindowManager与WindowManagerService的交互也是一个IPC的过程。
- SurfaceFlinger:SurfaceFlinger服务运行在Android系统的System进程中,它负责管理Android系统的帧缓冲区(Frame Buffer),Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户界面的。
- Surface:每个显示界面的窗口都是一个Surface。
- PhoneWindowManager:实现了窗口的各种策略。
- Choreographer:用于控制窗口动画、屏幕旋转等操作。
- DisplayContent:用于描述多屏输出相关信息。
- WindowState:描述窗口的状态信息以及和WindowManagerService进行通信,一般一个窗口对应一个WindowState。
- WindowToken:窗口Token,用来做Binder通信。
- Session:通信对象,App进程通过建立Session代理对象和Session对象通信,进而和WindowManagerService建立连接。
- 前面说到窗口的管理服务WindowManagerService运行在SystemServer进程,所以应用进程与WindowManagerService的交互是一个IPC的过程,具体流程如下所示:
image - 如上图所示,Activity持有一个Window,负责UI的展示与用户交互,里保存了很多重要的信息,如下所示:
- mWindow:PhoneWindow对象,继承于Window,是窗口对象。
- mWindowManager:WindowManagerImpl对象,实现WindowManager接口。
- mMainThread:Activity对象,并非真正的线程,是运行在主线程里的对象。
- mUIThread:Thread对象,主线程。
- mHandler:Handler对象,主线程Handler。
- mDecor:View对象,用来显示Activity里的视图。
- ViewRootImpl负责管理DecorView与WindowManagerService的交互,每次调用WindowManager.addView()添加窗口时,都会创建一个ViewRootImpl对象,它内部也保存了一些重要信息,如下所示:
- mWindowSession:IWindowSession对象,Session的代理对象,用来和Session进行通信,同一进程里的所有ViewRootImpl对象只对应同一个Session代理对象。
- mWindow:IWindow.Stubd对象,每个窗口对应一个该对象。
- 注:每个Activity对应一个Window,每个Window对应一个ViewRootImpl。
1.2 WindowManager介绍
- 窗口的操作被定义WindowManager中,WindowManager是一个接口,继承于ViewManager,实现类是WindowManagerImpl,实际上我们常用的功能,也是定义在ViewManager里的。
public interface ViewManager{ //添加View public void addView(View view, ViewGroup.LayoutParams params); //更新View public void updateViewLayout(View view, ViewGroup.LayoutParams params); //删除View public void removeView(View view); }
- WindowManager可以通过Context来获取,WindowManager也会和其他服务一样在开机时注册到ContextImpl里的map容器里,然后通过他们的key来获取。
windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- WindowManager的实现类是WindowManagerImpl,在WindowManagerImpl内部实际的功能是有WindowManagerGlobal来完成的,我们直接来分析它里面这三个方法的实现。
02.Window的添加流程
- 具体看一下下面的代码
public final class WindowManagerGlobal { public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { //校验参数的合法性 //ViewRootImpl封装了View与WindowManager的交互力促 ViewRootImpl root; View panelParentView = null; synchronized (mLock) { // Start watching for system property changes. 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)) { // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } // If this is a panel window, then find the window it is being // attached to for future reference. 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); } } } //通过上下文构建ViewRootImpl root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); //mViews存储着所有Window对应的View对象 mViews.add(view); //mRoots存储着所有Window对应的ViewRootImpl对象 mRoots.add(root); //mParams存储着所有Window对应的WindowManager.LayoutParams对象 mParams.add(wparams); } // do this last because it fires off messages to start doing things try { //调用ViewRootImpl.setView()方法完成Window的添加并更新界面 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } } }
- 在这个方法里有三个重要的成员变量:
- mViews存储着所有Window对应的View对象
- mRoots存储着所有Window对应的ViewRootImpl对象
- mParams存储着所有Window对应的WindowManager.LayoutParams对象
- 这里面提到了一个不是很熟悉的类ViewRootImpl,它其实就是一个封装类,封装了View与WindowManager的交互方式,它是View与WindowManagerService通信的桥梁。最后也是调用ViewRootImpl.setView()方法完成Window的添加并更新界面。
- 来看看这个方法的实现。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks { public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; //参数校验与预处理 ... // 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. //1. 调用requestLayout()完成界面异步绘制的请求 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(); //2. 创建WindowSession并通过WindowSession请求WindowManagerService来完成Window添加的过程 //这是一个IPC的过程。 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(); } } ... } } }
- 这个方法主要做了两件事:
- 1.调用requestLayout()完成界面异步绘制的请求,requestLayout()会去调用scheduleTraversals()来完成View的绘制,scheduleTraversals()方法将一个TraversalRunnable提交到工作队列中执行View的绘制。而TraversalRunnable最终调用了performTraversals()方法来完成实际的绘制操作。
- 2.创建WindowSession并通过WindowSession请求WindowManagerService来完成Window添加的过程这是一个IPC的过程,WindowManagerService作为实际的窗口管理者,窗口的创建、删除和更新都是由它来完成的,它同时还负责了窗口的层叠排序和大小计算等工作。
- 关于performTraversals()方法的实现,这里我们再简单提一下:
- 1.获取Surface对象,用于图形绘制。
- 2.调用performMeasure()方法测量视图树各个View的大小。
- 3.调用performLayout()方法计算视图树各个View的位置,进行布局。
- 4.调用performMeasure()方法对视图树的各个View进行绘制。
- 既然提到WindowManager与WindowManagerService的跨进程通信,我们再讲一下它们的通信流程。Android的各种服务都是基于C/S结构来设计的,系统层提供服务,应用层使用服务。WindowManager也是一样,它与 WindowManagerService的通信是通过WindowSession来完成的。
- 1.首先调用ServiceManager.getService("window")获取WindowManagerService,该方法返回的是IBinder对象,然后调用IWindowManager.Stub.asInterface()方法将WindowManagerService转换为一个IWindowManager对象。
- 2.然后调用openSession()方法与WindowManagerService建立一个通信会话,方便后续的跨进程通信。这个通信会话就是后面我们用到的WindowSession。
- 基本上所有的Android系统服务都是基于这种方式实现的,它是一种基于AIDL实现的IPC的过程。
- 关于aidl可以看我的这篇博客:Aidl详细介绍
public final class WindowManagerGlobal { public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); //获取WindowManagerService对象,并将它转换为IWindowManager类型 IWindowManager windowManager = getWindowManagerService(); //调用openSession()方法与WindowManagerService建立一个通信会话,方便后续的 //跨进程通信。 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) { //调用ServiceManager.getService("window")获取WindowManagerService,该方法返回的是IBinder对象 //,然后调用IWindowManager.Stub.asInterface()方法将WindowManagerService转换为一个IWindowManager对象 sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { sWindowManagerService = getWindowManagerService(); ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowManagerService; } } }
03.Window的删除流程
- Window的删除流程也是在WindowManagerGlobal里完成的。
public final class WindowManagerGlobal { public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { //1. 查找待删除View的索引 int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); //2. 调用removeViewLocked()完成View的删除, removeViewLocked()方法 //继续调用ViewRootImpl.die()方法来完成View的删除。 removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); } } private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } } }
- 再来看看ViewRootImpl.die()方法的实现。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks { boolean die(boolean immediate) { // Make sure we do execute immediately if we are in the middle of a traversal or the damage // done by dispatchDetachedFromWindow will cause havoc on return. //根据immediate参数来判断是执行异步删除还是同步删除 if (immediate && !mIsInTraversal) { doDie(); return false; } if (!mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(mTag, "Attempting to destroy the window while drawing!\n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } //如果是异步删除,则发送一个删除View的消息MSG_DIE就会直接返回 mHandler.sendEmptyMessage(MSG_DIE); return true; } void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { //调用dispatchDetachedFromWindow()完成View的删除 dispatchDetachedFromWindow(); } if (mAdded && !mFirst) { 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) { } } mSurface.release(); } } mAdded = false; } //刷新数据,将当前移除View的相关信息从我们上面说过了三个列表:mRoots、mParms和mViews中移除。 WindowManagerGlobal.getInstance().doRemoveView(this); } void dispatchDetachedFromWindow() { //1. 回调View的dispatchDetachedFromWindow方法,通知该View已从Window中移除 if (mView != null && mView.mAttachInfo != null) { mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); mView.dispatchDetachedFromWindow(); } mAccessibilityInteractionConnectionManager.ensureNoConnection(); mAccessibilityManager.removeAccessibilityStateChangeListener( mAccessibilityInteractionConnectionManager); mAccessibilityManager.removeHighTextContrastStateChangeListener( mHighContrastTextManager); removeSendWindowContentChangedCallback(); destroyHardwareRenderer(); setAccessibilityFocus(null, null); mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; mSurface.release(); if (mInputQueueCallback != null && mInputQueue != null) { mInputQueueCallback.onInputQueueDestroyed(mInputQueue); mInputQueue.dispose(); mInputQueueCallback = null; mInputQueue = null; } if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; } //调用WindowSession.remove()方法,这同样是一个IPC过程,最终调用的是 //WindowManagerService.removeWindow()方法来移除Window。 try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } // Dispose the input channel after removing the window so the Window Manager // doesn't interpret the input channel being closed as an abnormal termination. if (mInputChannel != null) { mInputChannel.dispose(); mInputChannel = null; } mDisplayManager.unregisterDisplayListener(mDisplayListener); unscheduleTraversals(); } }
- 来总结一下Window的删除流程:
- 1.查找待删除View的索引
- 2.调用removeViewLocked()完成View的删除,removeViewLocked()方法继续调用ViewRootImpl.die()方法来完成View的删除。
- 3.ViewRootImpl.die()方法根据immediate参数来判断是执行异步删除还是同步删除,如果是异步删除则则发送一个删除View的消息MSG_DIE就会直接返回。如果是同步删除,则调用doDie()方法。
- 4.doDie()方法调用dispatchDetachedFromWindow()完成View的删除,在该方法里首先回调View的dispatchDetachedFromWindow方法,通知该View已从Window中移除,然后调用WindowSession.remove()方法,这同样是一个IPC过程,最终调用的是WindowManagerService.removeWindow()方法来移除Window。
04.Window的更新流程
- 代码如下所示
public final class WindowManagerGlobal { 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的LayoutParams参数 view.setLayoutParams(wparams); synchronized (mLock) { //查找Viewd的索引,更新mParams里的参数 int index = findViewLocked(view, true); ViewRootImpl root = mRoots.get(index); mParams.remove(index); mParams.add(index, wparams); //调用ViewRootImpl.setLayoutParams()完成重新布局的工作。 root.setLayoutParams(wparams, false); } } }
- 再来看看ViewRootImpl.setLayoutParams()方法的实现。
public final class ViewRootImpl implements ViewParent, void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { synchronized (this) { //参数预处理 ... //如果是新View,调用requestLayout()进行重新绘制 if (newView) { mSoftInputMode = attrs.softInputMode; requestLayout(); } // Don't lose the mode we last auto-computed. if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); } mWindowAttributesChanged = true; //如果不是新View,调用requestLayout()进行重新绘制 scheduleTraversals(); } } }
- Window的更新流程也和其他流程相似:
- 1.更新View的LayoutParams参数,查找Viewd的索引,更新mParams里的参数。
- 2.调用ViewRootImpl.setLayoutParams()方法完成重新布局的工作,在setLayoutParams()方法里最终会调用scheduleTraversals()进行解码重绘制,scheduleTraversals()后续的流程就是View的measure、layout和draw流程了,这个我们在上面已经说过了。
- 通过上面分析,我们了解了Window的添加、删除和更新流程。那么我们来思考一个问题:既然说是Window的添加、删除和更新,那为什么方法名是addView、updateViewLayout、removeView,Window的本质是什么?🤔
- 事实上Window是一个抽象的概念,也就是说它并不是实际存在的,它以View的形式存在,每个Window都对应着一个View和一个ViewRootImpl,Window与View通过ViewRootImpl来建立联系。推而广之,我们可以理解WindowManagerService实际管理的也不是Window,而是View,管理在当前状态下哪个View应该在最上层显示,SurfaceFlinger绘制也同样是View。
其他介绍
01.关于博客汇总链接
02.关于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e