文章目录
- 流程概览
- 涉及模块
- 流程概览
- 应用端——window创建:Activity::attach
- 创建window流程
- setWindowManager,getWindowManager
- DecorView
- 应用端——window的显示流程:Activity::onResume
- ViewRootImpl::setView
- mWindowSession 是什么
- mWindow是什么
- 跨进程通信小结
- 流程小结
- 关键类小结
- 窗口添加——流程概览
- WindowManagerService::addWindow方法概览
- Token相关
- WindowState的创建与挂载
- WindowState挂载
- 挂载的位置
流程概览
涉及模块
WMS 负责管理设备上所有的 Window ,所以应用想显示一个 Window 则要通过 WMS 来完成。 而 WMS 毕竟还是上层,窗口的内容要显示到屏幕上,还需要 SurfaceFlinger 来处理。 整个窗口的显示逻辑会涉及到下图中的三个模块:
1、应用端:控制内部窗口的添加以及UI绘制逻辑。
2、WMS模块:作为系统窗口管理模式,处理应用端的窗口显示逻辑。
3、SurfaceFlinger模块:WMS只能控制系统层窗口逻辑,真正显示内容还需要原生驱动层的SurfaceFlinger来完成。
应用端与WMS通信是通过匿名Binder —> Session完成。
WMS与SurfaceFlinger的通信通过匿名Binder ——> Clinent 完成。
流程概览
Google把窗口的显示分为了三个流程:
-
addWindow流程:
应用在走到onResume生命周期时,viewrootImpl会向WMS发送addWindow请求添加窗口,这一阶段,WMS的处理为:
为应用端创建对应的windowstate并挂载。 -
relayoutWindow流程:
addWindow 流程后执行后,屏幕上就有新的 WindowState 添加了,WMS 也需要对屏幕上所有的窗口执行一遍 layout 来确定各个窗口所在的位置。
而应用端想要绘制 UI 数据,则也需要知道自己的窗口大小,位置信息,并且还需要一个 Surface 来承载 UI 数据。所以这一阶段 WMS 的处理为:- 为窗口申请Surface并返回给应用端
- 计算并返回窗口的大小,位置信息给应用端
-
finishDrawingWindow流程:
执行完上一流程后,应用端就可以执行View显示三部曲(测量、布局、绘制)来绘制UI了,绘制完成后需要将UI显示到屏幕上,这一步还需要WMS来通过SurfaceFlinger来显示这个Surface
应用端——window创建:Activity::attach
应用进程启动后,会执行 LaunchActivityItem 和 ResumeActivityItem 这2个事务,对应执行到 Activity 的 onCreate 和 onResume 生命周期,这其中肯定也涉及到了 Window 相关的操作。
LaunchActivityItem::executeActivityThread::handleLaunchActivityActivityThread::performLaunchActivityInstrumentation::newActivity -- 创建ActivityActivity::attach -- 创建WindowWindow::initWindow::setWindowManagerInstrumentation::callActivityOnCreate Activity::performCreateActivity::onCreate ResumeActivityItem::executeActivityThread::handleResumeActivityActivityThread::performResumeActivity Activity::performResume Instrumentation::callActivityOnResumeActivity::onResume WindowManagerImpl::addView -- 创建ViewRootImplWindowManagerGlobal::addView ViewRootImpl::setView -- 与WMS通信触发窗口的显示逻辑
这个调用链重点是:
1、创建Activity时在attach时,会创建对应的window,并setWindowManager,为activity绑定对应的窗口。注意,attach这个阶段activity还没执行到onCreate阶段,而是刚刚new出来,可见,activity与window的绑定是很早的,在activity刚刚new出来的attach阶段
。
2、在Activity的onResume阶段,执行 addView ,而这个 addView 后的逻辑才是触发 Window 显示流程,所以执行了 onResume 只是 Activity 可见,不代表 View 都显示了,这个时候都没触发 WMS 的绘制,如果后续的流程中出了问题,我们写在 XML 里的布局是不会显示在屏幕上的。window的显示在activity的onResume阶段
。
创建window流程
# ActivityThreadprivate Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {......// 定义windowWindow window = null;......// token传递的是ActivityRecord的token// 这里的 window 正常逻辑目前还为nullactivity.attach(...,r.token,, window, ...);......}# Activity// 定义Window对象private Window mWindow;final void attach(......) {......// 重点* 1. 创建 PhoneWindowmWindow = new PhoneWindow(this, window, activityConfigCallback);// 一些设置mWindow.setWindowControllerCallback(mWindowControllerCallback);// 重点* 2. 将Activity作为回调设置给WindowmWindow.setCallback(this);......// 重点* 3. 设置token为 ActivityRecordmToken = token;......// 重点* 4.1 set WindowManagermWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);......// 重点* 4.2 get WindowManagermWindowManager = mWindow.getWindowManager();......}public Window getWindow() {return mWindow;}
- 日常开发中,通过 Activity::getWindow 方法返回的其实是 PhoneWindow ,这是因为 Window 是个抽象类,而 PhoneWindow 是 Window 的唯一子类。
- 创建 PhoneWindow 后有一堆设置,这里需要注意 Window::setCallback 方法,是将 Activity 设置给了 Window,这里有什么用呢?
这个是因为 Activity 也实现了 Window.Callback 接口,所以能传递 this ,这个接口主要是处理 Input 事件的。
Input 事件的传递流程都是先走到 Window,因为在 WMS 模块没有 Activity 的概念,只有 Window ,那么最后是怎么走到 Activity 呢?就是这里设置的 setCallback 。当然这个在当前分析的addWindow 流程没有关系,但是需要有点印象。
- 这块token可以理解为ActivityRecord
- setWindowManager 和 getWindowManager 这个两个方法写在这乍一看有点矛盾,在一个地方 set 又 get 感觉很多余。这是因为这里 set 和 get 返回的对象,其实不是同一个对象,下边会讲。
setWindowManager,getWindowManager
# Window// 应用Tokenprivate IBinder mAppToken;private WindowManager mWindowManager;// wm :WindowManager对象,注意下传进来的值// appToken :这个就是AMS中与当前Activity对应的ActivityRecord// appName :Activity全类名public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {// 将ActivityRecord设置给mAppTokenmAppToken = appToken;mAppName = appName;mHardwareAccelerated = hardwareAccelerated;if (wm == null) {wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}// 根据强转也能看出 mWindowManager 是 WindowManagerImpl 的类型mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}public WindowManager getWindowManager() {return mWindowManager;}
这里将传递进来的 wm 强转成 WindowManagerImpl 后调用其 createLocalWindowManager方法。
看的出来这个 wm 就是 WindowManagerImpl 类型。
这里的 WindowManager 其实就是一个接口,看命令就 Window 的管理者,FrameWork 有很多跨进程通信的类命名方式就是 BP 端叫 XXManager 然后对应的 BN 端就是 XXManagerService 。
但是! 这里的 WindowManager 不是这样的,他真的就是一个单纯的 java 语言里的接口,定义了一些约束,然后由 WindowManagerImpl 实现。
另外这里是通过 “getSystemService” 这份方式获取的,也容易给人误解他是一个 Service 。其实这个 WindowManager 和其实现类 WindowManagerImpl 与 WindowManagerService 几乎一点关系都没有。
WindowManager 又实现了 ViewManager 接口,这个接口中定义了对 View 的三个操作:addView ,removeView ,updateViewLayout 。
继续看流程,
WindowManagerImpl::createLocalWindowManager 该方法重新创建返回了一个 WindowManagerImpl 对象。 所以说setWindowManager 和 getWindowManager 的不是同一个对象, WindowManagerImpl::createLocalWindowManager 方法如下:
# WindowManagerImplpublic WindowManagerImpl createLocalWindowManager(Window parentWindow) {return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);}private WindowManagerImpl(Context context, Window parentWindow,@Nullable IBinder windowContextToken) {mContext = context;mParentWindow = parentWindow;mWindowContextToken = windowContextToken;}
这边注意的是将 Window 设置给了 mParentWindow。 相当于通过新创建的PhonWindow创建了一个 WindowManagerImpl ,作为其mWindowManager的对象。
到这里创建Window相关的就分析完了,创建的这个Window其实是 PhoneWindow 。
DecorView
每个 Window 都拥有一个 View。
# PhoneWindow.java// This is the top-level view of the window, containing the window decor.// 翻译:这是窗户的顶层视图,包含窗户装饰。private DecorView mDecor;private void installDecor() {mForceDecorInstall = false;if (mDecor == null) {mDecor = generateDecor(-1);......}......}// 返回 DecorView@Overridepublic final @NonNull View getDecorView() {if (mDecor == null || mForceDecorInstall) {// 为空就创建installDecor();}return mDecor;}
这个 mDecor 是 DecorView, 本质上也是一个 帧布局的View,是UI视图树的根 ,在 PhoneWindow::generateDecor 方法赋值,这个方法有多处会执行,最常见的就是我们在 Activity::onCreate 里执行 setContentView 的时候。
# DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {......
}
也就是说 Window 通过 mDecor 的变量持有一个 View。
应用端——window的显示流程:Activity::onResume
这部分流程由应用端 ResumeActivityItem 开始事务触发,最终会执行到 Activity::onResume 生命周期,并且也会执行 ViewRootImpl::setView 方法,这个方法才是触发 窗口显示的真正逻辑。
# ActivityThreadpublic void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {......// 重点* 1. 触发onResumeif (!performResumeActivity(r, finalStateRequest, reason)) {return;}......// 拿到activityfinal Activity a = r.activity;......if (r.window == null && !a.mFinished && willBeVisible) {// 将window 设置到 activityRecord中r.window = r.activity.getWindow();// 获取DecorViewView decor = r.window.getDecorView();// 设置 View 不可见 decor.setVisibility(View.INVISIBLE);// 实际上是WindowManagerImplViewManager wm = a.getWindowManager();// 获取参数WindowManager.LayoutParams l = r.window.getAttributes();// DecorView设置给Activitya.mDecor = decor;// 重点* 2. 设置Activity的windowType,注意这个type,才是应用的窗口类型l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;......if (a.mVisibleFromClient) {if (!a.mWindowAdded) {// 重点* 3. 执行addView,并设置mWindowAdded=truea.mWindowAdded = true;wm.addView(decor, l);} else {......}}} else if (!willBeVisible) {......}......if (r.activity.mVisibleFromClient) {// 设置可见r.activity.makeVisible();}......}
这段代码信息量还挺多的,相关注释已经加在代码上了,这里对3个重点单独提一下:
-
1、performResumeActivity 会触发 onResume 。执行顺序在下面的 addView 之前,说明
onResume 只是 Activity 可见,而不是 UI 可见
-
2、Activity 的 WindowType 为 TYPE_BASE_APPLICATION = 1, 还有个 TYPE_APPLICATION=2 目前已知是在创建 ActivityRecord 时使用
-
3、通过 WindowManagerImpl::addView 触发后续逻辑
# WindowManagerImpl// 单例private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance()@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyTokens(params);// 这里的mParentWindow 就是PhoneWindowmGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());}
这个方法并没有啥复杂的,直接调到了 WindowManagerGlobal,不过这里也有2个需要注意的点:
- 1、WindowManagerGlobal 是个单例,那就是说一个进程仅此一个。
- 2、这里将mParentWindow传递了过去,上面分析的时候知道这个 mParentWindow 其实就是我们创建的 PhoneWindow。
# WindowManagerGlobal// 应用内 View 集合@UnsupportedAppUsageprivate final ArrayList<View> mViews = new ArrayList<View>();@UnsupportedAppUsage// 应用内 ViewRootImpl 集合private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();@UnsupportedAppUsage// 应用内 View 参数 集合private final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {......final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {// 重点* 1. 调整 window参数,parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {......}// 定义ViewRootImplViewRootImpl root;View panelParentView = null;synchronized (mLock) {......// 如果一个窗口执行过 addView 的话,再执行就报错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.}......IWindowSession windowlessSession = null;......// 对于应用来说 windowlessSession 是为null的if (windowlessSession == null) {// 重点* 2. 创建ViewRootImplroot = new ViewRootImpl(view.getContext(), display);} else {root = new ViewRootImpl(view.getContext(), display,windowlessSession);}// 设置参数到 decorViewview.setLayoutParams(wparams);// 重点* 3. 添加到对应集合,看得出来在WindowManagerGlobal中这3个对象应该是要一一对应的mViews.add(view);mRoots.add(root);mParams.add(wparams);// do this last because it fires off messages to start doing thingstry {// 重点* 4. ViewRootImpl::setViewroot.setView(view, wparams, panelParentView, userId);} catch (RuntimeException e) {......}}}
这段代码最重要的就是做了4件事:
1、Window::adjustLayoutParamsForSubWindow 调整参数,比如设置 token ,title,和硬件加速的标志位这3个比较重要的参数
2、ViewRootImpl 的创建 (这个类很重要)
3、WindowManagerGlobal 是进程单例维护了这个应用中多有的 DecorView ,而且看得出来定义了3个集合,且3个集合的元素是一一对应的
4、执行 ViewRootImpl::setView 方法,传递的 view 就是 PhoneWindow 持有的 DecorView 。 这里是应用端处理窗口的最重要一步,也是需要分析的主流程。
ViewRootImpl::setView
到这里要注意的,现在开始就没有对 Window 进行操作了,操作的是 DecorView 。
# ViewRootImpl// 与 WMS 通信的 binderfinal IWindowSession mWindowSession;// 对应的DecorViewView mView;public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {// 当前第一次执行肯定为nullif (mView == null) {// 重点* 将DecorView赋值给 mViewmView = view;......mAdded = true; // 表示已经addint res; // 定义稍后跨进程add返回的结果// 重点* 非常重要的方法,relayoutWindow和finishDrawingWindow都在这触发requestLayout(); InputChannel inputChannel = null; // 用于窗口接收inputif ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {inputChannel = new InputChannel();}......try {......// 重点* 这里通过binder通信,调用WMS的 addWindow方法res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls);......}// 后续流程与addWindow主流程无关,但是也非常重要......// 计算一次 Window的尺寸final WindowConfiguration winConfig = getConfiguration().windowConfiguration;mWindowLayout.computeFrames(mWindowAttributes, state,displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,mInsetsController.getRequestedVisibilities(),getAttachedWindowFrame(), 1f /* compactScale */, mTmpFrames);setFrame(mTmpFrames.frame);......if (res < WindowManagerGlobal.ADD_OKAY) {......// 对WMS调用addWindow后的结果判断是什么错误}......// DecorView::getParent 返回的是 ViewRootImpl 的原因view.assignParent(this); ......}}}
这个方法是核心方法,处理了很多事,注释都加上了。为了有一个宏观的印象,这里将其触发的各个调用链整理如下:
ViewRootImpl::setViewViewRootImpl::requestLayoutViewRootImpl::scheduleTraversals ViewRootImpl.TraversalRunnable::run --- Vsync相关--scheduleTraversalsViewRootImpl::doTraversalViewRootImpl::performTraversals ViewRootImpl::relayoutWindow --- relayoutWindowViewRootImpl::performMeasure --- View绘制三部曲ViewRootImpl::performLayoutViewRootImpl::performDraw ViewRootImpl::createSyncIfNeeded --- 绘制完成finishDrawingWindowSession.addToDisplayAsUser --- addWindow
这里要注意:虽然看顺序好像 addWindow 流程是在 relayoutWindow 执行前,但是因为 doTraversal 是异步的,所以还是先执行 addWindow 流程。
可以看到窗口显示的3个流程都在 ViewRootImpl::setView 里触发,在看着3个流程前,先看2个变量:
- 1、mWindowSession 是什么?
- 2、addToDisplayAsUser 放到第一个参数里的 mWindow 是什么?
mWindowSession 是什么
# ViewRootImplfinal W mWindow;final IWindowSession mWindowSession;public ViewRootImpl(Context context, Display display) {this(context, display, WindowManagerGlobal.getWindowSession(),false /* useSfChoreographer */);}public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,boolean useSfChoreographer) {mContext = context;mWindowSession = session;......mWindow = new W(this);......}
1、mWindowSession 是个 binder 对象,用于跨进程通信(与WMS)
2、mWindowSession 的赋值在 ViewRootImpl 构造方法
3、Activity 场景下分析,在 WindowManagerGlobal::addView 方法中构造 ViewRootImpl 是2个参数的构造方法,所以 mWindowSession 就是 WindowManagerGlobal::getWindowSession
接下来就需要看看 WindowManagerGlobal::getWindowSession 方法。
# WindowManagerGlobalprivate static IWindowSession sWindowSession;@UnsupportedAppUsagepublic static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {// Emulate the legacy behavior. The global instance of InputMethodManager// was instantiated here.// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsageInputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();IWindowManager windowManager = getWindowManagerService();// 赋值sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}});} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}// 返回return sWindowSession;}}
继续看 WindowManagerService::openSession 。
# WindowManagerService@Overridepublic IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);}
# Sessionclass Session extends IWindowSession.Stub implements IBinder.DeathRecipient {final WindowManagerService mService;......@Overridepublic int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);}
}
1、所以这里的 WindowManagerGlobal::getWindowSession 返回的就是一个 Session 对象。Session 继承 IWindowSession.Stub,并且内部持有 WMS 引用
2、应用端调用的 Session::addToDisplayAsUser 最终执行的是 WindowManagerService:: addWindow
3、 Session 是一个匿名 Binder
mWindow是什么
调用 Session::addToDisplayAsUser 方法传递了一个 mWindow ,咋一看还以为是把应用端的 Window 对象传递到 WMS 了,但是细想也不可能, WMS 模块的业务并不依赖应用端的 Window 对象,从上一小节的分析也看到了 mWindow 是 W 类型的变量,也是在 ViewRootImpl 的构造方法里赋值的,那这个W是什么呢?
# ViewRootImplfinal W mWindow;final IWindowSession mWindowSession;public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,boolean useSfChoreographer) {mContext = context;mWindowSession = session;......mWindow = new W(this);......}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;}......}
1、ViewRootImpl 下的 mWindow 是其内部类 W 的对象,这个 W 继承了 IWindow.Stub,那也是用于 binder 通信的
2、这个 W 是个匿名 Binder 作为 BN(binder native) 端,用于 WMS 调用应用端的相关方法
3、W 内部有一个 ViewRootImpl 弱引用。
跨进程通信小结
WMS 是实名 Binder Service 。从可行性上来说 ViewRootImpl 是完全可以直接通过 Binder 与 WMS 进行直接通信的,但是为什么要加这个 IWindowSession 呢?
这是因为 WMS 是核心服务,系统中有很多地方都需要与其通信,所以如果能减少直接与其 Binder 通信的频率也能提升系统效率。 所以 WMS 为每个 Activity(窗口)提供了一个匿名实现:IWindowSession ,处理一些“小请求”,减少各个进程直接与 WMS Binder 通信的频率。
这个和 SurfaceFlinger 的设计一致,SurfaceFlinger 也提供了一个匿名 Binder :Client 。
流程小结
应用端的流程在 ViewRootImpl::setView 方法中就结束了,内部会触发3大流程。
应用进程启动后,会执行 2 个事务,分别触发到 Activity 的 onCreate 和 onResume 2个常见的生命周期,所以这里分为了2个分支。
onCreate 分支:
(1)由 LaunchActivityItem 事务触发 handleLaunchActivity
(2)创建 Activity
(3)创建 Window,而 Window 是抽象类,PhoneWindow 唯一实现类。
(4)执行到 onCreate 生命周期
onResume 分支:
(1)由 ResumeActivityItem 事务触发 handleResumeActivity
(2)先触发 onResume 的执行流程
(3)执行 WindowManagerImpl::addView 实际上流程是交给了 WindowManagerGlobal::addView 处理
(4)创建核心类 ViewRootImpl
(5)执行关键函数 ViewRootImpl::setView ,这里会触发 WMS 三大流程
3大流程的逻辑还没看,但是可以先有个概念:
(1)addWindow 流程: ViewRootImpl 调用 Session::addToDisplayAsUser 触发 WindowManagerService::addWindow
(2)relayoutWindow 流程:ViewRootImpl 调用 Session::relayout 触发 WindowManagerService::relayoutWindow
(3)finishDrawingWindow 流程:ViewRootImpl 调用 Session::finishDrawing 触发 WindowManagerService::finishDrawingWindow
关键类小结
-
Activity :
是通过反射创建的 ,通过成员变量 mWindow 持有一个 Window
一个应用可以有多个 Activity ,而每个 Activity 的结构都是刚才分析的,这些 Activity 被存在 ActivityThread 下的 mActivities 的变量中 -
PhoneWindow,Window :Window 是个抽象类,唯一实现是 PhoneWindow ,通过成员变量 mDecor 持有一个 View
-
DecorView : 本质上也是一个 View 是开发常说的 “根View” ,Window 本身是没有内容的,真正 UI 载体就是这个 DecorView
-
ViewManager ,WindowManager : 前面2者都是接口类,WindowManagerImpl 实现其接口
-
WindowManagerImpl :WindowManagerImpl 表面上是管理 Window ,但是实际业务还是交给了 WindowManagerGlobal
-
WindowManagerGlobal :
- 应用内全局单例,是真正的应用内 Window 的总管家,但是其实管理的是 Window 下的 DecorView
- 当应用内有 Window 创建后,WindowManagerImpl 会调用
- WindowManagerGlobal::addView 方法将 DecorView 传递过去
WindowManagerGlobal 会为每个 DecorView 创建一个 ViewRootImpl 来管理这棵“View树” - 内部有3个集合,将 Window 的 DecorView 参数 LayoutParams 和新创建的 ViewRootImpl 一一添加到对应的集合中
-
ViewRootImpl :在 GUI 系统中是应用端的核心类,管理一个“View树”,负责这个“View树”的绘制等事务,同时还需要控制其与 WMS 通信
-
Session : WMS 为应用端的一个窗口单独开发的跨进程通信通道
-
W : 应用端的 Binder Service ,会传递到 WMS 中,这样 WMS 就可以与 应用端的一个窗口进行通信。
窗口添加——流程概览
首先从结果上对比下应用启动后窗口的区别:
红色部分就是启动应用后多出来的部分,在 DefaultTaskDisplayArea 节点下多出来这么一个层级:
TaskActivityRecordWindowState
其中 Task 和 ActivityRecord 是如何挂载上去的在【Activity启动流程】已经介绍了,当前要分析的 addWindow 流程最重要的目标就是分析窗口对应的 WindowState 是如何创建并且挂载到窗口树中的。
也就是这一变化:
这个流程逻辑相对简单,整个流程框图如下:
-
1、应用端 Activity 执行到 onResume 说明 Activity 已经可见,下面就需要处理可见的内容
-
2、应用端 Session 调用到 WindowManagerService::addWindow 方法
-
3、WMS 处理 addWindow 流程也就做了2件事:
- 创建出对应的 WindowState
- 挂载到层级树中(挂载到对应的 WindowToken 下)
上边知道流程已经执行到 ViewRootImpl::setView 来触发 addWindow 流程,回忆一下应用端的调用:
# ViewRootImplfinal W mWindow;public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {......res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,mTempControls, attachedFrame, compatScale);......}
这里有几个参数比较重要: mWindow : 用于 WMS 与应用端通信 mWindowAttributes : DecorView 的参数 getHostVisibility() :可见性 inputChannel:Input 通路。
看到你这些参数有个疑问:
明明是 addWindo 流程,但是到了 WindowManagerImpl 就变成了 addView 传递的也是 DecoreView ,再到和 WMS 通信的时候,参数里连 DecoreView 都不剩了,这怎么能叫 addWindow 流程呢?
WindowManagerService::addWindow方法概览
接上知道 Session::addToDisplayAsUser 方法调用的是 WindowManagerService::addWindow ,先看一下这个方法。
# WindowManagerService// 保存应用端 ViewRootImpl 和 WindowState 的映射关系/** Mapping from an IWindow IBinder to the server's Window object. */final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {......// 1.1 权限检查int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);if (res != ADD_OKAY) {return res;}// 父窗口,应用 Activity 窗口逻辑是没有父窗口的WindowState parentWindow = null;......// 拿到当前窗口类型final int type = attrs.type;......synchronized (mGlobalLock) {......// 1.2 如果窗口已经添加,直接returnif (mWindowMap.containsKey(client.asBinder())) {// 日志ProtoLog.w(WM_ERROR, "Window %s is already added", client);return WindowManagerGlobal.ADD_DUPLICATE_ADD;}......ActivityRecord activity = null;// 是否为 hasParentfinal boolean hasParent = parentWindow != null;// 2.1 拿到tokenWindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// Activity 没有父窗口,这里也为nullfinal int rootType = hasParent ? parentWindow.mAttrs.type : type;......if (token == null) {......if (hasParent) {// Use existing parent window token for child windows.// 2.2子窗口用父窗口的 tokentoken = parentWindow.mToken;} else if (...) {......} else {// 2.3 系统窗口会创建 tokenfinal IBinder binder = attrs.token != null ? attrs.token : client.asBinder();token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();}} else if (rootType >= FIRST_APPLICATION_WINDOW&& rootType <= LAST_APPLICATION_WINDOW) {......} else if......// 忽略其他各种创建对 token的处理// 3.1 创建 WindowStatefinal WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);......final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();// 调整window的参数displayPolicy.adjustWindowParamsLw(win, win.mAttrs);......// 1.3 验证Window是否可以添加,主要是验证权限res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);if (res != ADD_OKAY) {// 如果不满足则直接returnreturn res;} final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if (openInputChannels) {// 4.1 Input 事件输入通道win.openInputChannel(outInputChannel);}......// 3.2 创建SurfaceSessionwin.attach();// 3.3 窗口存入mWindowMapmWindowMap.put(client.asBinder(), win);......// 3.4 窗口挂载win.mToken.addWindow(win);displayPolicy.addWindowLw(win, attrs);......// 4.2 处理窗口焦点切换boolean focusChanged = false;if (win.canReceiveKeys()) {focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,false /*updateInputWindows*/);if (focusChanged) {imMayMove = false;}}......// 调整父容器下的元素层级win.getParent().assignChildLayers();// 4.3 更新inut焦点if (focusChanged) {displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,false /*updateInputWindows*/);}// 4.4 更新input窗口displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);// 窗口添加logProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));......}Binder.restoreCallingIdentity(origId);return res;}
这个方法就是 addWindow 流程的核心方法了,代码很多,保留了下面4个主要逻辑:
- 1、检验处理:操作权限检验,判重校验以限制应用端的一个rootview只能执行一次。
- 2、Token处理
- token其实就是WindowToken类别(ActivityRecord的父类)
- 获取token,如果是子窗口从父窗口拿,没有就从参数拿
- 如果是系统窗口,就会根据窗口类型创建出一个WindowToken
- 3、WindowState处理
- new WindowState
- 执行WindowState::attach,创建出SurfaceSession
- 将新建的WindowState和W映射,存入mWindowMap中。
- 窗口挂载
- 4、Input和焦点处理
当前分析的 addWindow 主流程,所以分析2,3亮点,也就是 Token 和 WindowState 的处理逻辑。
Token相关
当前分析的场景,这个 Token 就是 Activity 启动流程中创建 ActivityRecord 时创建的 Token ,而 ActivityRecord 是 WindowToken 的子类。
在 【WindowContainer窗口树】介绍过,WindowState 的父节点大部分情况是 WindowToken ,而且在上一篇看到 dump 启动应前后的窗口树区别,明确知道 WindowState 是挂载到 ActivityRecord 下的,
在分析 WindowState 的创建和挂载前,需要先给它找到它的父节点: WindowToken 。这也是 WindowManagerService::addWindow 方法中比较靠前执行的逻辑。
根据上一小节的分析,当前场景的 Token 来自参数“attrs.token” 。这个参数是应用端传递过来的,上一篇在分析 WindowManagerGlobal::addView 方法的时候提到在Window::adjustLayoutParamsForSubWindow 方法对赋值 token 给参数,现在看一下这个方法。
# Window// 应用Tokenprivate IBinder mAppToken;void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {......if (wp.token == null) {wp.token = mContainer == null ? mAppToken : mContainer.mAppToken; // activity的window在这里设置token}......}
mContainer 唯一赋值的地方在 Window::setContainer 方法,当前没调用,所以 wp.token 最终的值是为 mAppToken ,而mAppToken 的赋值在给 Window 设置 WindowManager 的时候赋值,也就是setWindowManager 方法,这里的 token 就是 ActivityRecord 的 token 。
下面这张图可以更直观的看到 Token 的传递:
-
1、WindowToken 内部有个成本变量 token ,ActivityRecord 是其子类
-
2、Activity 启动过程中会先创建 ActivityRecord ,在创建 ActivityRecord 的时候会创建一个匿名 Token 对象,并保存在变量 token 中
-
3、随着启动流程的执行,会在 ActivityTaskSupervisor::realStartActivityLocked 方法里构建事务,这个时候 token 就被保存在 ClientTransaction 的成员变量 mActivityToken
-
4、ClientTransaction 提供了一个 getActivityToken 方法返回 mActivityToken 。这个方法在具体的事务执行时,比如 LaunchActivityItem::execute 方法执行,会作为参数传递过去
-
5、LaunchActivityItem::execute 方法会构建一个 ActivityClientRecord ,构建方法需要 Token 参数,这个时候 Token 就被保存在 ActivityClientRecord 的成员变量 token 中
-
6、接下里就到了应用进程,应用进程执行 ActivityThread::performLaunchActivity 方法开始处理 Activity 启动流程,ActivityClientRecord 作为参数被传递了过来
-
7、ActivityThread::performLaunchActivity 方法内部会执行 Activity::attach 方法,这个方法需要一个 Token 作为参数,传递的就是从 ActivityClientRecord 里取出的 token
-
8、Activity::attach 方放会将 Token 赋值给成员变量 mToken
-
9、Window 创建后会执行 Window::setWindowManager ,这个时候会将 mToken 作为参数传递进去,保存在 Window 的成员变量 mAppToken 中
-
10、在执行 WindowManagerGlobal::addView 时会执行 Window::adjustLayoutParamsForSubWindow 调整参数,这个时候 Token 就被复制到 WindowManager.LayoutParams 下的 token 变量中
-
11、执行 addWindow 流程时,WindowManager.LayoutParams 会被传递到 WMS ,这样 Token 也就被传递了过去
WindowState的创建与挂载
addWindow 流程中 WindowState 的创建与挂载是重点,在 WindowManagerService::addWindow 方法中,执行了 WindowState 的创建,代码如下:
# WindowManagerServicepublic int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {......// WindowToken相关处理// 创建WindowStatefinal WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);......}
这里注意几个参数,然后直接看WindowState的构造方法
# WindowStatefinal IWindow mClient;@NonNull WindowToken mToken;// The same object as mToken if this is an app window and null for non-app windows.// 与 mToken 相同的对象(如果这是应用程序窗口),而对于非应用程序窗口为null// 说人话就是应用窗口才有ActivityRecordActivityRecord mActivityRecord;// 层级final int mBaseLayer;WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,PowerManagerWrapper powerManagerWrapper) {......mClient = c;......// 保存tokenmToken = token;// 只有 ActivityRecord 重写了 asActivityRecord 其他默认返回努力了mActivityRecord = mToken.asActivityRecord();......//子窗口处理if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW){......}else {// Activity的窗口指为 2 * 10000 + 1000 = 21000mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;......}}
创建 WindowState 有2个重要的参数 :client,和token。这个 client 代表着客户端也就是 ViewRootImpl 的内部类 W ,另一个参数就是上节的 Token 。
WindowState 以后会经常看到,不过当前只要知道在 WindowManagerService::addWindow 会创建出一个 WindowState 对象即可。
WindowState挂载
WindowState 创建好后自然是需要挂载到窗口树的,操作也很简单,直接添加到对应的 (ActivityRecord)WindowToken 下就好。
# WindowManagerService// ViewRootImpl和WindowState的mapfinal HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {......// 窗口已经添加,直接returnif (mWindowMap.containsKey(client.asBinder())) {// 打印logProtoLog.w(WM_ERROR, "Window %s is already added", client);return WindowManagerGlobal.ADD_DUPLICATE_ADD;}......// WindowToken相关处理......// WindowState的创建// WindowState的挂载win.attach();// 1. 存进mapmWindowMap.put(client.asBinder(), win);......// 2. 挂载win.mToken.addWindow(win);......}
- 1、 在看挂载前先看一下 mWindowMap 这个数据结构,key 是一个 IBinder,value 是 WindowState ,这边将新创建的 WindowState 作为 value 添加到了 map 中,前面说过 client是应用端 ViewRootImpl 下的 “W”这个类,也就是说在 WMS 中应用端的这个 ViewRootImpl 和为其创建的 WindowState 已经被记录在 mWindowMap 中了。
在执行WMS::addWindow方法开始的时候就会尝试通过 clent 从 mWindowMap 获取值,如果获取到了说明已经执行过 addWindow 则进行 return 不执行后面逻辑。
- 2、这里是窗口的挂载,“win.mToken” 这里的 mToken 刚刚看到是创建 WindowState 的时候传递的 token 也就是 ActivityRecord (WindowToken)。也就是说调用的是 ActivityRecord::addWindow 方法进行挂载的。
# ActivityRecord@Overridevoid addWindow(WindowState w) {super.addWindow(w);checkKeyguardFlagsChanged();}
直接调用其父类方法,ActivityRecord 父类是 WindowToken,也就是说调用的是ActivityRecord::addWindow 方法进行挂载的。
# ActivityRecord@Overridevoid addWindow(WindowState w) {super.addWindow(w);checkKeyguardFlagsChanged();}
直接调用其父类方法,ActivityRecord 父类是 WindowToken
# WindowTokenvoid addWindow(final WindowState win) {// WindowState 挂载日志ProtoLog.d(WM_DEBUG_FOCUS,"addWindow: win=%s Callers=%s", win, Debug.getCallers(5));if (win.isChildWindow()) {// Child windows are added to their parent windows.// 子窗口的父类应该是WindowState所以不执行后续return;}// This token is created from WindowContext and the client requests to addView now, create a// surface for this token.// 真正添加进子容器if (!mChildren.contains(win)) {// 日志ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);// 挂载(添加进孩子容器),有一个比较方法addChild(win, mWindowComparator);// 记录有窗口边框mWmService.mWindowsChanged = true;// TODO: Should we also be setting layout needed here and other places?}}
执行完 WindowContainer::addChild 方法后 WindowState 已经被添加到层级树中了,挂在到对应的 ActivityRecord 下。
当然这里需要注意 WindowToken::addWindow 最终也是调用父类 WindowContainer::addChild 将 WindowState 添加到自己的孩子中,这里传递了一个mWindowComparator。
挂载的位置
WindowContainer::addChild 方法被定义在基类,也就是容器添加孩子时都会按一定规则添加,当然默认其实还是按顺序,但是有的时候也有特殊情况,所以这个方法提供了一个参数,使得可以在具体场景控制具体的添加逻辑。
# WindowContainerprotected void addChild(E child, Comparator<E> comparator) {// 检查子元素是否已经被其他容器拥有,如果是,则抛出异常if (!child.mReparenting && child.getParent() != null) {throw new IllegalArgumentException("addChild: container=" + child.getName()+ " is already a child of container=" + child.getParent().getName()+ " can't add to container=" + getName());}// 初始化插入位置为-1,表示尚未找到合适的插入位置int positionToAdd = -1;// 如果有比较器则进行比较// 遍历当前容器中的所有子元素if (comparator != null) {final int count = mChildren.size();// 使用比较器比较待插入的子元素和当前容器中的子元素for (int i = 0; i < count; i++) {// 如果比较结果小于0,表示待插入元素应该位于当前元素之前if (comparator.compare(child, mChildren.get(i)) < 0) {positionToAdd = i;break;}}}// 没有比较器或者比较的结果还是-1 ,则添加到最后(大部分场景)if (positionToAdd == -1) {mChildren.add(child);} else {// 如果比较器计算出了准确位置,则按要求添加mChildren.add(positionToAdd, child);}// Set the parent after we've actually added a child in case a subclass depends on this.// 调用孩子容器设置当前容器为其父节点child.setParent(this);}
1、方法目的就是添加子元素到父容器中,但是可以根据 comparator 比较规则添加到正确的位置
2、比较方式很简单,拿当前需要添加的元素和容器内其他元素逐个比较,如果比较 comparator 返回值小于0,则添加到“被比较”的元素前面
3、有2种情况,是按顺序添加到容器末尾
- 3.1 没有比较器。positionToAdd 为默认值 -1
- 3.2 和每个元素比较的返回值都大于0,说明要添加其后面,这个时候 positionToAdd 还是为默认值 -1
4、setParent 调用孩子容器设置当前容器为其父节点,另外还会将 mSyncState 变量设置为 SYNC_STATE_WAITING_FOR_DRAW
当前场景,父容器 ActivityRecord 还是是空的,所以没什么意义。不过既然看到这里,就继续分析,根据分析,当前逻辑调用的比较器是 WindowToken下的 mWindowComparator 。