WMS详解(1) - 启动与Activity窗口创建

Leo 2021年12月15日 1,914次浏览

简述

WindowManagerService,Android的窗口管理,WMS错综复杂,与 ActivityManagerService、InputManagerService、SurfaceFlinger关系也很紧密,本文深入探讨一下WMS的管理设计,以便理解View、Activity、Window、Task等之间的关系。
首先还是从WindowManagerService服务的启动开始,在Android开机流程中,我们知道WMS是在引导服务和核心服务启动之后才会开始的:

private void startOtherServices(@NonNull TimingsTraceAndSlog t){
    ...
    t.traceBegin("StartWindowManagerService");
    // WMS needs sensor service ready
    mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
            new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
    */
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
            mSystemServerExt.getSubPhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
            DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
            /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    t.traceEnd();

    t.traceBegin("SetWindowManagerService");
    mActivityManagerService.setWindowManager(wm);
    t.traceEnd();

    t.traceBegin("WindowManagerServiceOnInitReady");
    wm.onInitReady();
    t.traceEnd();
}

这里眼尖的同学发现了,WMS的启动必须等待SensorService的启动完毕,为什么呢,后面给出答案

WSM启动

从上面也看出了,WMS的启动是先调用了其静态方法main, 并传入context, inputManagerActivityTaskManager等,还有一个WindowManagerPolicy子类PhoneWindowManager的对象

WindowManagerService.main
public static WindowManagerService main(final Context context,final
InputManagerService im,
final boolean showBootMsgs,final boolean onlyCore,
        WindowManagerPolicy policy,
        ActivityTaskManagerService atm){
        return main(context,im,showBootMsgs,onlyCore,policy,atm,
        SurfaceControl.Transaction::new,Surface::new,
        SurfaceControl.Builder::new);
        }
/**
 * Creates and returns an instance of the WindowManagerService. This call
 * allows the caller
 * to override factories that can be used to stub native calls during test.
 */
@VisibleForTesting
public static WindowManagerService main(final Context context,final
InputManagerService im,
final boolean showBootMsgs,final boolean onlyCore,
        WindowManagerPolicy policy,
        ActivityTaskManagerService atm,
        Supplier<SurfaceControl.Transaction>transactionFactory,
        Supplier<Surface> surfaceFactory,
        Function<SurfaceSession, SurfaceControl.Builder>
        surfaceControlFactory){
// 使用"android.display"线程初始化WindowManagerService
// 初始化没有结束时,调用main的线程会被阻塞
        DisplayThread.getHandler().runWithScissors(()->
        sInstance=new WindowManagerService(context,im,showBootMsgs,
        onlyCore,policy,
        atm,transactionFactory,surfaceFactory,
        surfaceControlFactory),0);
        return sInstance;
        }

这⾥使⽤的双冒号"::"、Supplier以及Function都是java8引⼊的函数式编程语法。值得注意的是runWithScissors⽅法。

DisplayThread.getHandler
public final class DisplayThread extends ServiceThread {
    private static DisplayThread sInstance;
    private static Handler sHandler;

    private DisplayThread() {
// DisplayThread runs important stuff, but these are not as
        important as things running in
// AnimationThread. Thus, set the priority to one lower.
        super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false
                /*allowIo*/);
    }

    private static void ensureThreadLocked() {
        if (sInstance == null) {
            sInstance = new DisplayThread();
            sInstance.start();
            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
            sHandler = new Handler(sInstance.getLooper());
        }
    }

    public static Handler getHandler() {
        synchronized (DisplayThread.class) {
            ensureThreadLocked();
            return sHandler;
        }
    }
...
}

由此可以看出DisplayThread其实就是Android⾥的"android.display"线程,优先级仅仅⽐THREAD_PRIORITY_DISPLAY低⼀档。

Handler.runWithScissors
/**
 * @hide
 */
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
        if (r == null) {
        throw new IllegalArgumentException("runnable must not be null");
        }
        if (timeout < 0) {
        throw new IllegalArgumentException("timeout must be non-negative");
        }
        if (Looper.myLooper() == mLooper) {
        r.run();
        return true;
        }
        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
        }

这⾥可以看到该⽅法⾸先检查了参数合法性,然后判断当前允许的线程是否是Handler处理线程:

  1. 如果是Handler处理线程,则执⾏runnable
  2. 如果不是,则创建⼀个BlockingRunnable,执⾏其postAndWait⽅法
    postAndWait名字中可以推测,必定是需要等待指定runnableHandler处理线程上执⾏完毕后,调⽤
    runWithScissors的线程才能继续运⾏。
private static final class BlockingRunnable implements Runnable {
    private final Runnable mTask;
    private boolean mDone;

    public BlockingRunnable(Runnable task) {
        mTask = task;
    }

    @Override
    public void run() {
        try {
            mTask.run();
        } finally {
            // runnable执行完毕后标记mDone为true,然后通知所以等待线程
            synchronized (this) {
                mDone = true;
                notifyAll();
            }
        }
    }

    public boolean postAndWait(Handler handler, long timeout) {
        // 当handler中已经存在此runnable时,返回false
        if (!handler.post(this)) {
            return false;
        }
        synchronized (this) {
            if (timeout > 0) {
                final long expirationTime = SystemClock.uptimeMillis() +
                        timeout;
                while (!mDone) {
                    long delay = expirationTime -
                            SystemClock.uptimeMillis();
                    if (delay <= 0) {
                        return false; // timeout
                    }
                    try {
                        // 等待delay时长
                        wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            } else {
                while (!mDone) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        return true;
    }
}

所以runWithScissors⽅法为啥会标记为hide呢,明明很好的实现了将某个任务放到指定线程执⾏并等待该任务
执⾏完毕后继续。

  1. 考虑到如果等待超时,postAndWait返回的也是false,但是对应runnablemessage仍旧处于Handler的
    MessageQueue之中,这样该runnable最终还是会被执⾏的。
  2. 可能造成死锁,因为runWithScissors所处线程会⼀直等待,除⾮超时。⽐如任务所处的handler的
    Looper被调⽤了quit()退出时。我们知道quit⽅法是会清理Looper的MessageQueue中所有消息,如果
    此时timeout的时间设置的是0,那么runWithScissors⽅法所处的线程会⼀直等到天荒地⽼。除⾮Looper
    退出时是调⽤的quitSafely(), 该⽅法只会清空MessageQueue中当前时间点之后的message,在这之前的
    message仍会被执⾏。
    ⽽我们知道,DisplayThread代表线程是不会退出的(除⾮关机), 所以这⾥调⽤runWithScissors是安全的。
private WindowManagerService(Context context,InputManagerService
        inputManager,
        boolean showBootMsgs,boolean onlyCore,WindowManagerPolicy policy,
        ActivityTaskManagerService atm,
        Supplier<SurfaceControl.Transaction>transactionFactory,
        Supplier<Surface> surfaceFactory,
        Function<SurfaceSession, SurfaceControl.Builder>
        surfaceControlFactory){
        // 在LockGuard中创建代表WINDOW的Lock, 可以帮助检测系统服务内部锁的机制
        installLock(this,INDEX_WINDOW);
        ...
        // 标记当前设备是否是可触摸的
        mInTouchMode=context.getResources().getBoolean(
        com.android.internal.R.bool.config_defaultInTouchMode);
        // 设置是否允许触摸,现在手机、平板设备一般都是可触摸的
        inputManager.setInTouchMode(mInTouchMode);
        ...
        // 通过surfaceControlFactory可以直接生成SurfaceControl.Builder的对象
        mSurfaceControlFactory=surfaceControlFactory;
        // 其实是实现了Supplier接口的SurfaceControl.Transaction::new
        mTransactionFactory=transactionFactory;
        // 其实是实现了Supplier接口Surface::new
        mSurfaceFactory=surfaceFactory;
        // 创建Transaction对象, 注意每次调用get方法生成的都是新的
        SurfaceControl.Transaction对象
        mTransaction=mTransactionFactory.get();
        // 这个policy就是PhoneWindowPolicy
        mPolicy=policy;
        // 创建WindowAnimator
        mAnimator=new WindowAnimator(this);
        // 创建RootWindowContainer
        mRoot=new RootWindowContainer(this);
        ...
        // 創建WindowSurfacePlacer,如其名,用於定位窗口及其表面
        // 通过计算窗口的框架来设置窗口的位置,然后根据这些框架来定位Surface
        mWindowPlacerLocked=new WindowSurfacePlacer(this);
        // 創建TaskSnapshotController,用於获取相应任务的快照(位图)并将其放入缓存
        mTaskSnapshotController=new TaskSnapshotController(this);
        ...
        // 创建WakeLock,用于在转屏等动作时保持cpu不休眠
        mScreenFrozenLock=mPowerManager.newWakeLock(
        PowerManager.PARTIAL_WAKE_LOCK,"SCREEN_FROZEN");
        mScreenFrozenLock.setReferenceCounted(false);
        // 用于管理监听Display的层次属性变化的监听器
        mDisplayNotificationController=new
        DisplayWindowListenerController(this);
        ...
        // 创建WakeLock,在必要时保持屏幕常量
        mHoldingScreenWakeLock=mPowerManager.newWakeLock(
        PowerManager.SCREEN_BRIGHT_WAKE_LOCK|
        PowerManager.ON_AFTER_RELEASE,TAG_WM);
        mHoldingScreenWakeLock.setReferenceCounted(false);
        // 用于执行不需要持有WindowManager的锁的窗口动画
        mSurfaceAnimationRunner=new
        SurfaceAnimationRunner(mTransactionFactory,
        mPowerManagerInternal);
        ...
        // 用于任务拖动定位的控制器
        mTaskPositioningController=new TaskPositioningController(
        this,mInputManager,mActivityTaskManager,mH.getLooper());
        // 该类处理并组合从多个视图中生成的拖动事件,然后向任何已经注册了回调的ondragdroplistener触发事件
        mDragDropController=new DragDropController(this,mH.getLooper());
        // 用于强制显示高刷新率的包的Denylist
        mHighRefreshRateDenylist=
        HighRefreshRateDenylist.create(context.getResources());
        ...
        // 创建DisplayAreaPolicyProvider,用于后续的DisplayAreaPolicy的构建
        mDisplayAreaPolicyProvider=DisplayAreaPolicy.Provider.fromResources(
        mContext.getResources());
        ...
        // 设置全局阴影
        setGlobalShadowSettings();
        mAnrController=new AnrController(this);
        // 管理创建和释放启动窗口Surface
        mStartingSurfaceController=new StartingSurfaceController(this);
        }

这⾥省略的很多设置代码,仅贴了⼀些⽐较重要或者有趣的类对象初始化。

Activity的Window创建

对于每个应用,Activity在onCreate⽅法中setContentView设置了UI布
局,之后在该Activity第⼀次resume时,初始化ViewRootImpl,当该Activity是应⽤进程第⼀个被resume的Activity时,建⽴进程与WMS的通信通道,也就是WindowSession
最后ViewRootImpl的setView⽅法中,通过binder调⽤WindowManager的addToDisplay,之后进⼊到SystemServer进程调⽤到WMS.addWindow,在这⾥创建该Activity对应的WindowState,并保存在mWindowMap中。
如下即是WMS中addWindow的关键代码:

public int addWindow(Session session,IWindow client,LayoutParams
        attrs,int viewVisibility,
        int displayId,int requestUserId,InsetsState
        requestedVisibility,
        InputChannel outInputChannel,InsetsState outInsetsState,
        InsetsSourceControl[]outActiveControls){
        ......
synchronized (mGlobalLock){
        ......
final DisplayContent displayContent=
        getDisplayContentOrCreate(displayId,attrs.token);
        ......
        ActivityRecord activity=null;
final boolean hasParent=parentWindow!=null;
        WindowToken token=displayContent.getWindowToken(
        hasParent?parentWindow.mAttrs.token:attrs.token);
final int rootType=hasParent?parentWindow.mAttrs.type:
        type;
        boolean addToastWindowRequiresToken=false;
final IBinder windowContextToken=attrs.mWindowContextToken;
        if(token==null){
        if(hasParent){
        ......
        }else{
        // 一般走这里创建WindowToken
final IBinder binder=attrs.token!=null?
        attrs.token:client.asBinder();
        token=new WindowToken(this,binder,type,false,
        displayContent,
        session.mCanAddInternalSystemWindow,
        isRoundedCornerOverlay);
        }
        }
        ...
// 初始化WindowState
final WindowState win=new WindowState(this,session,client,
        token,parentWindow,
        appOp[0],attrs,viewVisibility,session.mUid,userId,
        session.mCanAddInternalSystemWindow);
        ......
        // 创建SurfaceSession, 用来和SurfaceFlinger通信
        win.attach();
        // 保存WindowState
        mWindowMap.put(client.asBinder(),win);
        ......
        win.mToken.addWindow(win);
        ......
        ......
        }

我们可以看到WMS中⼏个关键的类:WindowState,WindowToken,ActivityRecord等。之前我们就绘制过
这个类图:

1. WindowToken: 是在WindowManagerService 中定义的⼀个基类,顾名思义,它是⽤来标识某⼀个窗
⼝。可以把WindowToken看成是⼀个显⽰令牌,⽆论是系统窗⼝还是应⽤窗⼝,添加新的窗⼝时需要使
⽤这个令牌向WMS表明⾃⼰的⾝份,添加窗⼝(addWindow)时会创建WindowToken,销毁窗⼝的时候
移除WindowToken(removeWindowToken⽅法)。
2. ActivityRecord(原AppWindowToken): 顾名思义,它是⽤来标识app, 跟准确的说法,是⽤来标识某个具
体的Activity. App每个的Activity对应⼀個ActivityRecord。其中的appToken為IApplicationToken.Stub
类型,有了它就可以找到对应的ActivityRecord.
从上⾯的关系图也能发现,这个WindowToken中token是关键点,我们尚未理清这个值的含义,接下来我们分析⼀下这个token的来源:

PhoneWindow的创建

简单回顾⼀下 Activity 对应的 PhoneWindow的创建,注意这⾥仅仅是App进程中的Window.
我们从handleLaunchActivity开始:

public Activity handleLaunchActivity(ActivityClientRecord r,
	PendingTransactionActions pendingActions, Intent customIntent) {
......
	final Activity a = performLaunchActivity(r, customIntent);
......
}

performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent
        customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        ......
        Activity activity = null;
        try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 通过反射生成Activity
        activity = mInstrumentation.newActivity(
        cl, component.getClassName(), r.intent);
        ......
        }
        try {
        // 创建应用Application
        Application app = r.packageInfo.makeApplication(false,
        mInstrumentation);
        ......
        if (activity != null) {
        ......
        // 这里传入的window一般是null的
        activity.attach(appContext, this, getInstrumentation(),
        r.token,
        r.ident, app, r.intent, r.activityInfo, title,
        r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window,
        r.configCallback,
        r.assistToken, r.shareableActivityToken);
        ......
synchronized (mResourcesManager) {
        // 将该Activity保存至mActivities集合中,key值就是
        ActivityClientRecord中的token
        mActivities.put(r.token, r);
        }
        }
        ......
}
        

每次调⽤performLaunchActivity就⼀定会创建⼀个新的Activity对象,并保存在mActivities集合中。

Activity.attach
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor
        voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback,
        IBinder assistToken,
        IBinder shareableActivityToken) {
        ......
        // 创建Activity对应的PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode !=
        WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
        }
        ......
        // 设置PhoneWindow的WindowManager
        mWindow.setWindowManager(
        (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponent.flattenToString(),
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        // 当Activity的parent不为空时,该Activity的PhoneWindow对应的容器就是其parent对应的窗口
        if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
        }
        ......
    }

可以看到⼀个Activity对应⼀个PhoneWindow, ⽽且PhoneWindow是在Activity launch的时候才创建的。

WindowToken中token的作⽤

我们假设添加window的Activity是该应⽤第⼀个被resume的activity:

        //WMS.addWindow函数中:
final IBinder binder = attrs.token != null ? attrs.token :
        client.asBinder();
        token = new WindowToken(this, binder, type, false, displayContent,
        session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
        // WindowToken.java
        WindowToken(WindowManagerService service, IBinder _token, int type, boolean
        persistOnEmpty,
        DisplayContent dc, boolean ownerCanManageAppTokens, boolean
        roundedCornerOverlay) {
        this(service, _token, type, persistOnEmpty, dc,
        ownerCanManageAppTokens,
        roundedCornerOverlay, false /* fromClientToken */, null /*
options */);
        }
        WindowToken(WindowManagerService service, IBinder _token, int type, boolean
        persistOnEmpty,
        DisplayContent dc, boolean ownerCanManageAppTokens, boolean
        roundedCornerOverlay,
        boolean fromClientToken, @Nullable Bundle options) {
        super(service);
        token = _token;
        windowType = type;
        mOptions = options;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        mRoundedCornerOverlay = roundedCornerOverlay;
        mFromClientToken = fromClientToken;
        if (dc != null) {
        dc.addWindowToken(token, this);
        }
    }

WindowToken的构造函数和其初始化的传参中可以看到,WindowToken中的token就是attrs的token,或者是client(IWindow)转换来的。

LayoutParams中的token

溯源addWindow,ViewRootImpl.setView时,会将该LayoutParams作为参数传⼊,注意,有两个LayoutParams,⼀个是WindowManager.LayoutParams, ⼀个是其⽗类ViewGroup.LayoutParams:

ViewRootImpl.setView也是在 WindowManagerGlobal.addView 时调⽤的,也就是ActivityThread处理该Activity的Resume状态时:

public void handleResumeActivity(ActivityClientRecord r, boolean
        finalStateRequest,
        boolean isForward, String reason) {
        ......
final Activity a = r.activity;
        ......
        if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        // 现将该View设置为不可见
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        // 从这里拿到的params, 所以token也需要查一下这个LayoutParams
        // 最后可以发现是在PhoneWindow创建时候创建的默认值,token还是null的
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        // 注意这里type表示为应用窗口
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        if (a.mVisibleFromClient) {
        if (!a.mWindowAdded) {
        a.mWindowAdded = true;
        // 这里的wm是Activity中保存的WindowManager, 是该Activity被
        创建
        // 后调用其attach函数创建Window时的WindowManagerImpl,这一部
        分我
        // 们在Activity的显示(1)中有分析
        wm.addView(decor, l);
        ......
}

可以看出只有当该Activity的window是尚未被添加且此次resume状态时是需要可⻅的时候,才会将该Activity对应View添加⾄WindowManager中:

// WindowManagerImpl.addView
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams
        params) {
        applyTokens(params);
        // 这里的WMI是在Activity调用attach时通过createLocalWindowManager创建的
        // mParentWindow就是创建该WMI的window,即Activity里的PhoneWindow
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(),
        mParentWindow,
        mContext.getUserId());
        }
private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
        if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be
        WindowManager.LayoutParams");
        }
final WindowManager.LayoutParams wparams =
        (WindowManager.LayoutParams) params;
        // Only use the default token if we don't have a parent window and
        a token.
        if (mDefaultToken != null && mParentWindow == null && wparams.token
        == null) {
        // token在此赋值吗?其实一般不是的,这个mDefaultToken只有
        AccessibilityService中会设置
        // 所以mDefaultToken还是null的
        wparams.token = mDefaultToken;
        }
        wparams.mWindowContextToken = mWindowContextToken;
        }

虽然不是在此追踪,但是我们可以发现mGlobal.addView中的LayoutParams仍然是ViewGroup.LayoutParams类型,其并不带有token成员。

// WindowManagerGlobal.java
public void addView(View view,ViewGroup.LayoutParams params,
        Display display,Window parentWindow,int userId){
        ......
    // 因为WindowManager.LayoutParams是ViewGroup.LayoutParams子类,所以可以强制转换
    // 但是token默认是null的
    final WindowManager.LayoutParams wparams=
            (WindowManager.LayoutParams)params;
            // parentWindow一般不为null,如果是ApplicationType的window,这就是resume
            的Activity里的PhoneWindow自身
            if(parentWindow!=null){
            // 调整 WindowManager.LayoutParams
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
            }else{
            ......
            synchronized (mLock){
            // 2.3 创建ViewRootImpl
            root=new ViewRootImpl(view.getContext(),display);
            ......
            root.setView(view,wparams,panelParentView,userId);
            ......
            }
        }

注意每个Activity对应⼀个PhoneWindow, ⼀个PhoneWindow对应⼀个WindowManager(即WindowManagerImpl), ⽽PhoneWindow继承⾃Window。PhoneWindow在其对应的Activity被调⽤attach时
创建, 然后会创建WindowManagerImpl,保存在PhoneWindowmWindowManager成员中。

// Window.java
// Activity.attach -> PhoneWindow.setWindowManager->
public void setWindowManager(WindowManager wm,IBinder appToken,String
        appName,
        boolean hardwareAccelerated){
        // 这个就是Activity中的 mToken, 对应的是
        LocalActivityManager.LocalActivityRecord
        // 顺便说一下这个token是在startActivity时创建的
        mAppToken=appToken;
        mAppName=appName;
        mHardwareAccelerated=hardwareAccelerated;
        if(wm==null){
        wm=
        (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager=
        ((WindowManagerImpl)wm).createLocalWindowManager(this);
        }
        // ActivityThread.handleResumeActivity->WindowManagerImpl.addView-
        >WindowManagerGlobal.addView->
        void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp){
        CharSequence curTitle=wp.getTitle();
        // 在handleResumeActivity我们可以看到这个type被指明为TYPE_BASE_APPLICATION
        if(wp.type>=WindowManager.LayoutParams.FIRST_SUB_WINDOW&&
        wp.type<=WindowManager.LayoutParams.LAST_SUB_WINDOW){
        ......
        }else{
        // 到目前为止,token确实是null的
        if(wp.token==null){
        // mContainer是包含此窗户的容器,没有设置时,DecorWindow将作为顶级窗口
        wp.token=mContainer==null?mAppToken:
        mContainer.mAppToken;
        }
        ......
        ......
        }

⼀般Window的mContainer是null的,所以wp.token就是Window的mAppToken,也即Activity中的mToken, 对应LocalActivityManager.LocalActivityRecord, 通过此变量可以快速找到Activity、ActivityInfo、Window等。所以最后Activity对应的WindowState中对应的WindowToken⾥的token成员变量就是Activity中的mToken.

Activity的View与Window的关系

在应⽤的⼀个Activity对象即将resume时,会通过WindowManagerImpl将View与该Activity对应的PhoneWindow关联起来。这部分发⽣WindowManagerGlobal.addView中:

// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {
        ......
synchronized (mLock) {
        // 创建ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        ......
        root.setView(view, wparams, panelParentView, userId);
        ......
        }
    }

这⾥传⼊的View 就是 r.window.getDecorView() 获取的decorView; ⽽parentWindow就是该Activity⾸次
launch时创建的PhoneWindow(如果没有parent Activity时)
简单回顾下 PhoneWindow 的 DecorView是怎么来的:

  1. 在Activity被launch后,会调⽤其onCreate⽅法
  2. 应⽤会在这个⽅法中调⽤setContentView, 将该Activity对应的View或者layout资源ID传⼊
  3. 然后在调⽤该Activity对应PhoneWindow的setContentView⽅法
    1. 如果是⾸次调⽤,则先调⽤installDecor⽅法创建DecorView
    2. 之后通过PhoneWindow中的mLayoutInflater将该View实例化
    3. 最后将该View保存在PhoneWindow的ViewGroup对象mContentParent``中 这部分的流程⽐如installDecor`⽅法我们之后讨论View的⽣成时再详细分析。所以简单来说,Activity对应的
      layout视图作为⼀个⼦视图保存在这个Activity对应PhoneWindow的DecorView中。
IWindowSession - PhoneWindow与WMS的交互

Activity对应的Window和WMS的交互肯定是双向的,那么:

  1. Activity对应的PhoneWindow需要保存WMS的client端
  2. WMS需要保存该PhoneWindow的client端
    毫⽆疑问,PhoneWindow中保存WMS的client端就是对应的IWindowManager, 保存在WindowManagerGlobal
    中。⽽PhoneWindow会⽤在其对应的ViewRootImpl中继承了IWindow.Stub的W类的对象,通过binder传递到WMS中,作为PhoneWindow在WMS中的client端:
// ViewRootImpl.java
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;
        ......
        // 创建W对象
        mWindow = new W(this);
        ......
        }
public void setView(View view, WindowManager.LayoutParams attrs, View
        panelParentView,
        int userId) {
        .......
        res = mWindowSession.addToDisplayAsUser(mWindow,
        mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(),
        userId,
        mInsetsController.getRequestedVisibility(),
        inputChannel, mTempInsets,
        mTempControls);
        ......
        }


由此可⻅,ViewRootImpl发挥这⾄关重要的作⽤,其不仅包含Activity对应的View, PhoneWindow及该
Window的属性LayoutParams, 还保存着作为WMS中该Activity的Window的服务端W类的对象。
之前我们略有分析mWindowSession的创建,这⾥我们直接给出时序图:

之前我们也说过:

  1. IWindowManager对应WindowManagerService, 应⽤进程端通过这个接⼝即可向WMS传递消息.
  2. IWindowSession通过这个Session和WMS中的Session交互.暂时还不理解这个作⽤
  3. W extends IWindow 对应应⽤进程端的窗⼝, WMS通过这个向应⽤进程的Window发送消息.
    现在我们分析IWindowSession的⽅法其实不难理解,IWindowSession是为了⽅便管理某个应⽤的某个Window
    与WMS通信的,虽然最后也是与WMS通信,但是将通信过程也视为⼀个对象,⽅便了管理。

小结

本⽂中分析了Android显⽰框架中Framework层最重要的WMS服务启动过程,以及复习了Activity窗⼝的创建过
程。

WMS服务启动
  1. WMS服务是在引导服务和核⼼服务启动之后启动的,⽽且必须等待Sensor服务的启动完毕。
  2. WMS服务的启动⾸先初始化了"android.display"线程,然后将启动过程放在此线程中处理,主线程阻塞
    等待该启动完毕。
  3. WMS服务的创建过程:
    1. 创建了WindowAnimator
    2. 创建RootWindowContainer
    3. 创建WindowSurfacePlacer,如其名,⽤於定位窗⼝及其表⾯
    4. 创建TaskSnapshotController,⽤於获取相应任务的快照(位图)并将其放⼊缓存
    5. 设置全局阴影等等
  4. WMS服务创建完毕后,通过ServiceManager将其发布,服务名为Context.WINDOW_SERVICE
Activity、Window与WindowManagerService
  1. Activity在第⼀次启动后,调⽤attach⽅法时,会创建PhoneWindow以及对应的WindowManagerImpl
  2. 在Activity第⼀次resume时,调⽤该Activity的WindowManagerImpl创建ViewRootImpl
    1. 在创建ViewRootImpl之前,会通过adjustLayoutParamsForSubWindow将该Activity的mToken保
      存在PhoneWindow的LayoutParams中
    2. 创建ViewRootImpl过程中,会通过IWindowManager与WMS通信,创建IWindowSession, ⽤于该
      Activity的Window和WMS通信
  3. 创建ViewRootImpl之后,再通过期间创建的IWindowSession与将该Activity的IWindow、相关Window属性传递给WMS.

后记
这个系列源于一些笔记和看源码的心得,可能会有些杂乱和口语化、笔者属菜鸡,若有错误,请各位大佬及时指出。