在了解完Activity发动流程后,现在目标使用的进程现已发动了,可是离用户在屏幕上看到Activity下的UI内容还有一段距离。 一个窗口想要显现在屏幕上,还需求经过3大过程:

【Android 13源码剖析】WMS-增加窗口(addWindow)流程-1-使用进程处理

    1. addWindow流程

    这一步是创立 WindowState,而且挂载到窗口树上。

    1. relayout流程

    addWindow履行后,WindowState就创立和挂载好了,可是WindowState毕竟也是一个容器,没有真正的UI内容。 履行relayout流程时会触发真正持有UI数据的Surface的创立,然后会将这个Surface回来到使用进程,使用进程在进行View的制作三部曲。 除了创立Surface的逻辑,relayoutWindow流程还会触发窗口位置的摆放逻辑。

    1. finishDrawing流程

    真正显现到屏幕上的内容不是Activity也不是Window,而是View树制作的内容。 经过上面2步,使用进程现已有Surface了, 而且履行制作三部曲,也便是说View树的UI数据也就都制作到Surface下的buff中了。制作完结就需求通知SurfaceFlinger进行合成了, 只要经过SurfaceFlinger处理后,才干真正显现到屏幕上。

绝大部分情况下,一个窗口的显现,这三步是是必经流程。(开机动画是直接经过SurfaceFlinger制作的)

本篇剖析第一步:addWindow流程

当前为Activity短暂的终身系列的第三块内容, 主张先看完 WindowContainer窗口层级Activity发动流程

1. 概述

先比照一下一个使用发动后窗口的区别:

【Android 13源码剖析】WMS-增加窗口(addWindow)流程-1-使用进程处理

赤色部分便是发动使用后多出来的部分,在 DefaultTaskDisplayArea 节点下多出来这么一个层级:

Task
    ActivityRecord
        WindowState (便是那个 9c20028)

其中 Task 和 ActivityRecord 是怎样挂载上去的在Activity发动流程现已介绍了,本篇要剖析的 addWindow 的流程最重要的便是搞理解窗口对应的WindowState是怎样创立而且挂载到窗口树中的。

整个流程框图如下:

【Android 13源码剖析】WMS-增加窗口(addWindow)流程-1-使用进程处理

    1. 使用进程首先会创立出一个的Window
    1. 履行WindowManagerGlobal::addView办法,终究触发ViewRootImpl::setView办法来触发夸进程通讯,通知WMS履行addWindow逻辑
    1. 使用和WMS通讯是经过Session这个类,详细是调用了 Session::addToDisplayAsUser这个办法
    1. system_server进程的WMS履行addWindow办法时,会根据参数创立出一个WindowState,而且将其挂载到对应的WindowToken下(也便是挂载到窗口树中)

后边的内容也是围绕着这4点做详细解释首先介绍App进程的处理,然后介绍system_server进程的处理。

2. APP进程流程

先看使用层是需求做哪些事:使用进程发动后,会履行LaunchActivityItem和ResumeActivityItem 这2个业务,对应履行到Activity的onCreate和onResume生命周期,这其中肯定也触及到了Window相关的操作。

调用链如下:

LaunchActivityItem::execute
    ActivityThread::handleLaunchActivity
        ActivityThread::performLaunchActivity
            Instrumentation::newActivity      --- 创立Activity
            Activity::attach                  --- 创立Window
                Window::init
                Window::setWindowManager
            Instrumentation::callActivityOnCreate  
                Activity::performCreate
                    Activity::onCreate 
ResumeActivityItem::execute
    ActivityThread::handleResumeActivity
        ActivityThread::performResumeActivity   
            Activity::performResume   
                Instrumentation::callActivityOnResume
                    Activity::onResume        
        WindowManagerImpl::addView           --- 创立ViewRootImpl
            WindowManagerGlobal::addView   
                ViewRootImpl::setView        --- 与WMS通讯 addView

根据这个调用链可知:先履行onResume,再履行addView。所以履行了onResume仅仅Activity可见,不代表View都显现了,或许都还没触发WMS的制作,假如后续的任何一个当地出了问题,咱们写在XML里的布局都不会显现出来。(以前写App的时分以为履行了onResume屏幕上就显现UI了)

2.1 创立Window逻辑

这块的履行是在 LaunchActivityItem 业务的履行流程中,到Activity的onCreate的生命周期之间, 详细的位置能够看上面的调用链,开始撸代码.

# ActivityThread
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            ......
            // 界说window
            Window window = null;
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                // 正常不履行这儿
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            ......
            // 留意token传递的是ActivityRecord的token
            // 这儿的window正常逻辑为null
            activity.attach(...,r.token,, window, ...);
            ......
    }
# Activity
    final void attach(......) {
        ......
        // 创立PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        // 一些设置
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        // 留意这边将Activity设置为setCallback
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ......
        // 设置window的token为 ActivityRecord
        mToken = token;
        ......
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        ......
    }

咱们在Activity经过getWindow办法回来的便是这个mWindow,首先得确认这个,别剖析了半天剖析错了Window目标,毕竟framework的代码这么多。

这儿有个面试点,在Activity::attach中看到mWindow 原來是一个PhoneWindow的目标,PhoneWindow是Window的仅有子类,Window是个抽象类,所以也的确没有办法直接new。

然后是一堆设置,这儿需求留意 setCallback 办法,是将Activity设置给了Window,这儿有什么用呢? 像configruation的改动和input工作的传递流程都是先走到Window的,由于在WMS模块没有Activity的概念,只要Window,那么最终是怎样走到Activity呢?便是这儿设置的setCallback。当然这个在当前剖析的addWindow流程没有关系,可是需求有点形象。

再下面的setWindowManager和getWindowManager两个办法也很有意思,由于咋一看有点对立,在一个当地set又get感觉很剩余,由于这儿set和get回来的目标,其实不是同一个目标。

2.1.1 setWindowManager,getWindowManager

# Window
    // 使用Token
    private IBinder mAppToken;
    // wm :WindowManager目标,留意下传进来的值
    // appToken :这个便是AMS中与当前Activity对应的ActivityRecord
    // appName :Activity全类名
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        // 将ActivityRecord设置给mAppToken
        mAppToken = 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办法。

该函数从头创立回来了一个 WindowManagerImpl 目标。 所以说setWindowManager 和 setWindowManager 的不是同一个目标, WindowManagerImpl::createLocalWindowManager办法如下:

# WindowManagerImpl
    public 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 接下来看APP层的addWindow是怎样触发的。

主张要理清楚 Window, PhoneWindow ,WindowManagerImpl,WindowManager这几个类的区别和联系,不要搞混了。

2.2 addWindow相关

履行到onCreate之前, 现已创立好了Window,所以咱们在Activity::onCreate能够把咱们的XML布局设置曩昔,也能经过 getWindow来做一些操作。

Window创立好后就要履行 addWindow逻辑了,根据调用链,是 ResumeActivityItem业务触发的,这个业务终究会履行到 Activity::onResume生命周期。

接下来看一遍代码的履行流程:

# ActivityThread
    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, String reason) {
        ......
        // 触发onResume
        if (!performResumeActivity(r, finalStateRequest, reason)) {
            return;
        }
        ......
        // 拿到activity
        final Activity a = r.activity;
        ......
        if (r.window == null && !a.mFinished && willBeVisible) {
            // 将本地的window设置到activityRecord中
            r.window = r.activity.getWindow();
            // 获取DecorView
            View decor = r.window.getDecorView();
            // 设置不行见  在后边调用Activity::makeVisible会设为可见
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            // 获取参数
            WindowManager.LayoutParams l = r.window.getAttributes();
            // DecorView设置给Activity
            a.mDecor = decor;
            // 设置Activity的windowType,留意这个type,才是使用的窗口类型
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            ......
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    // 要点:履行addView,并设置mWindowAdded=true
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
    }

主要是一些属性的设置然后履行addView, 这儿比较需求留意的便是Activity的windowType为TYPE_BASE_APPLICATION = 1, 还有个TYPE_APPLICATION=2,现在已知是在创立ActivityRecord时使用。 然后咱们现已知道wm便是WindowManagerImpl了,可是不应该是addWindow么,怎样成addView了呢?接着去看下面流程。

# WindowManagerImpl
    // 单例
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance()
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyTokens(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

这个办法并没有啥复杂的,直接调到了WindowManagerGlobal,不过这儿也有2个需求留意的点:

    1. WindowManagerGlobal是个单例,那便是说一个进程仅此一个
    1. 这儿将mParentWindow传递了曩昔,上面剖析的时分知道这个mParentWindow其实便是咱们创立的PhoneWindow
# WindowManagerGlobal
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        ......
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            // 调整window参数,设置token,比方title,和硬件加速的标志位
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            ......
        }
        ViewRootImpl 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) {
                // 要点* 创立ViewRootImpl
                root = new ViewRootImpl(view.getContext(), display);
            } else {
                root = new ViewRootImpl(view.getContext(), display,
                        windowlessSession);
            }
            // 设置参数到 decorView
            view.setLayoutParams(wparams);
            // 增加到对应调集,看得出来在WindowManagerGlobal中这3个目标应该是要逐个对应的
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            // do this last because it fires off messages to start doing things
            try {
                // 要点 * 调用ViewRootImpl::setView
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

这段代码最重要的便是做了2件事:

    1. ViewRootImpl的创立 (ViewRootImpl在整个WMS体系中是非常重要的一个类)
    1. 履行ViewRootImplL::setView办法, 这儿也是使用进程处理的终点,剩下的便是跨进程交给WMS处理了

2.3 ViewRootImpl::setView

# ViewRootImpl
    final IWindowSession mWindowSession;
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            // 当前第一次履行肯定为null
            if (mView == null) {
                mView = view;
                ......
                mAdded = true; // 表示现已add
                int res; // 界说稍后跨进程add回来的成果
                requestLayout();  // 非常重要的办法--请求布局更新
                InputChannel inputChannel = null; // input工作相关
                if ((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 Rect displayCutoutSafe = mTempRect;
                state.getDisplayCutoutSafe(displayCutoutSafe);
                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调用后的成果判断是什么错误
                }
                ......
                view.assignParent(this); //这便是为什么decorView调用getParent回来的是ViewRootImpl的原因
                ......
            }
        }
    }

这个办法是中心办法,处理了很多事,都加载备注上了。为了有一个宏观的形象,这儿将其触发的各个调用链整理出来。

ViewRootImpl::setView
    ViewRootImpl::requestLayout
        ViewRootImpl::scheduleTraversals             
            ViewRootImpl.TraversalRunnable::run          --- Vsync相关--scheduleTraversals
                ViewRootImpl::doTraversal
                  ViewRootImpl::performTraversals 
                     ViewRootImpl::relayoutWindow        --- relayoutWindow
                     ViewRootImpl::performMeasure        --- View制作三部曲
                     ViewRootImpl::performLayout
                     ViewRootImpl::performDraw        
                     ViewRootImpl::createSyncIfNeeded    --- 制作完结finishDrawing
    WindowSession.addToDisplayAsUser                     --- addWindow
    WindowLayout::computeFrames                          --- 窗口巨细核算

这儿要留意:虽然看顺序如同 addWindow流程是在relayoutWindow履行前,可是由于 doTraversal是异步的,所以还是先履行addWindow流程的。

回到当前主题,继续跟踪addWindow流程,也便是addToDisplayAsUser,看来上面的setView办法还有2个点不清楚:

  1. mWindowSession是什么?
  2. 参数里的mWindow是什么?

2.3.1 ViewRootImpl的mWindowSession是什么

# ViewRootImpl
    final 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);
            ......
        }

WindowManagerGlobal::addView下结构ViewRootImpl的经过2个参数的结构办法,所以他的session便是WindowManagerGlobal.getWindowSession()

这儿提一嘴,既然会这么规划,那么阐明在Framework中肯定不是这一种方式获取session,比方画中画便是另一种,以后会提到,当前留个形象。

# WindowManagerGlobal
    @UnsupportedAppUsage
    public 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 @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
# WindowManagerService
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
    }
# Session
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    final WindowManagerService mService;
    ......
    @Override
    public 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);
    }
}

所以这儿的WindowManagerGlobal::getWindowSession回来的便是一个Session目标。Session承继IWindowSession.Stub,而且内部持有WMS引证。

2.3.2 ViewRootImpl的mWindow是什么

调用addToDisplayAsUser这个办法传递的mWindow,他并不是前面剖析的Activity的那个Window,上面mWindow也是在ViewRootImpl的结构办法里赋值的。那这个W是什么呢?

# ViewRootImpl
        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;
            }
        }

所以这儿的mWindow仅仅一个内部类W的目标,这个W承继了IWindow.Stub,那也是用例binder通讯的,W内部有一个ViewRootImpl弱引证。

2.4 APP进程小结

addWindow的流程,在APP进程到此就完毕了,后边的逻辑由WMS履行。

2.4.1 二级框图

【Android 13源码剖析】WMS-增加窗口(addWindow)流程-1-使用进程处理

使用进程发动后,会履行2个业务,别离触发到Activity的 onCreate和onResume2个常见的生命周期,所以这儿分为了2个分支。

onCreate 分支

    1. 首先肯定是要创立Activity的
    1. 然后创立出Window,Window是抽象类,PhoneWindow是Window的仅有实现类。
    1. 履行到 onCreate 生命周期

onResume 分支

    1. 先触发了 onResume 的履行流程
    1. 履行WindowManagerImpl::addView
    1. 创立中心类 ViewRootImpl
    1. 履行要害函数 ViewRootImpl::setView ,跨进程通讯后续流程就由WMS履行了

2.4.2 知识点小结:

一个进程内有个Window的总管家:WindowManagerGlobal。当Activity创立并初始化PhoneWindow后,WindowManagerImpl会调用WindowManagerGlobal的addView办法,将后续流程交给其处理。

WindowManagerGlobal会创立一个ViewRootImpl,WindowManagerGlobal内部还有3个调集,将Window的DecorView,参数LayoutParams和新创立的ViewRootImpl逐个增加到对应的调集中。

后续流程由ViewRootImpl进行,ViewRootImpl的addView方式会经过Session终究调用到WMS的addWindow办法。

    1. Activity里的window其实是PhoneWindow,由于Window是抽象类,而PhoneWindow是其仅有子类
    1. Window的windowManager是WindowManagerImpl,内部有2个重要成员,DevordViw和WindowManagerImpl,Window自身并没有内容所以DevordViw才是UI的实践载体
    1. WindowManagerGlobal是单例类,一个进程只要一个,内部保护了3个调集

还有3个重要的办法:

Activity::attach

    1. 创立了PhoneWindow
    1. 设置了WindowManagerImpl作为WindowManager

WindowManagerGlobal::addView

    1. 创立ViewRootImpl
    1. 履行ViewRootImpl::setView

ViewRootImpl::setView

    1. IWindowSession::addToDisplayAsUser : 代表着addWindow流程在App进程完毕,后边由WMS进行
    1. requestLayout:请求更新布局,触发relayoutWindow流程
    1. computeFrames : 核算窗口巨细

ViewRootImpl::setView 这个办法比较长,这也是我现在比较熟悉的几个工作,或许还有重要分支被我疏忽了。

其实有一个疑问,明明addWindo流程,可是到了WindowManagerImpl就变成了addView,传递的也是DecoreView,再到和WMS同信的时分,参数里连DecoreView都不剩了,这怎样能叫addWindow流程呢? 带着这个疑问下一篇将介绍WMS到底是怎样做的。

【Android 13源码剖析】WMS-增加窗口(addWindow)流程-1-使用进程处理

【Android 13源码剖析】WMS-增加窗口(addWindow)流程-2-SystemService进程处理