Android R WindowManagerService模块(2) Window的添加过程

3,222 阅读11分钟

窗口的添加,站在用户角度看,是开启一个新界面;站在开发者角度看,是通过API创建了一个新的Activity或窗口;站在系统实现角度看,则并非如此简单,本篇文章的目的,就是弄明白,当添加一个窗口时,系统相关管理者做了哪些工作。

1.添加窗口API

WindowManager是Android中提供给APP用来创建窗口的。在结构上,WindowManager继承于ViewManager,从API看,只提供了三个接口用来给APP添加或移除window:

abstract void addView(View view, ViewGroup.LayoutParams params)
abstract void removeView(View view)
abstract void updateViewLayout(View view, ViewGroup.LayoutParams params)

上面方法中也可以知道,在创建窗口时,我们只需要考虑这个窗口要展示的View、以及窗口的属性(如大小、类型,全部放置在WindowManager.LayoutParams中)。

WindowManager.LayoutParams是ViewGroup.LayoutParams的子类,该类用来携带创建Window时所设置的所有属性,如Window类型、属性、格式、Flag......

1.1.创建基本窗口

创建一个Window,只需要如下几步即可:

    private void addWindow() {
        // 1.获取WindowManager对象
        WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        // 2.获取Window.LayoutParams对象
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        // 3.定义Window要展示的View
        LinearLayout linearLayout = (LinearLayout) View.inflate(this,R.layout.window_layout,null);
        // 4.添加View
        wm.addView(linearLayout, layoutParams);
    }

以上示例第2步中,创建WindowManager.LayoutParams对象时使用了默认构造函数,除此之外,WindowManager API提供了7个构造方法用来创建WindowManager.LayoutParams:

WindowManager.LayoutParams()
WindowManager.LayoutParams(int _type)
WindowManager.LayoutParams(int _type, int _flags)
WindowManager.LayoutParams(int _type, int _flags, int _format)
WindowManager.LayoutParams(int w, int h, int _type, int _flags, int _format)
WindowManager.LayoutParams(int w, int h, int xpos, int ypos, int _type, int _flags, int _format)
WindowManager.LayoutParams(Parcel in)

这些参数表示:

  • _type: 窗口类型,用来计算层级,Android中定义了三大类、20几小类Window类型:
    • Application Window:[FIRST_APPLICATION_WINDOW,LAST_APPLICATION_WINDOW],[1,99];
    • Sub Window: [FIRST_SUB_WINDOW, LAST_SUB_WINDOW],[1000,1999];
    • SystemWindows: [FIRST_SYSTEM_WINDOW, LAST_SYSTEM_WINDOW ], [2000,2999];
  • _flag: 窗口的标记,会根据携带的标记值设置不同的功能;
  • _format: Bitmap的格式,PixelFormat中定义的常量;
  • xpos/ypos: Window的x、y坐标。 接下来就从WindowManager.addView()这个方法开始,看起Window是如何创建的。

2.Window的添加过程

下面我们从WindowManagerImpl.addView()方法开始,对添加窗口过程进行分析。

2.1.WindowManagerImpl.addView()

在framework层实现中,WindowMananger是一个接口,客户端得到的WindowMananger对象实际上是WindowManangerImpl实例,因此客户端执行WindowManager.addView()后,将会进入到WindowManagerImpl.addView()中:

// frameworks/base/core/java/android/view/WindowManagerImpl.java

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        // 如果不存在父Window且存在默认Token,会将mDefaultToken设置给params.token。一般都为null
        applyDefaultToken(params);
        // 进入WindowManagerGlobal
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

以上方法中,又会调用进入mGlobal#addView(),mGlobal是WindowManagerGlobal的实例,它通过单例方式提供给WindowMangerImpl:

// frameworks/base/core/java/android/view/WindowManagerImpl.java

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

WindowManagerGlobal类相对于WindoweMangerImpl来说,它的操作不需要依赖任何的Context对象。这个单例也是相对于WindoweMangerImpl而言的,在Activity启动时,系统会为每一个Activity或Window创建一个WindoweMangerImpl对象,但一个进程内,只有一个WindowManagerGlobal类实例。

2.2.WindowManagerGlobal.addView()

WindowManagerGlobal.addView()如下:

// frameworks/base/core/java/android/view/WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            // 如果存在父window,会将params调整一致,存在父Window时,wparams.token就是在此处设置
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // 设置HARDWARE_ACCELERATED标记
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
       		......
            // 创建一个ViewRootImpl,表示一个Viwe层级的根View
            root = new ViewRootImpl(view.getContext(), display);
            // 把params设置给View
            view.setLayoutParams(wparams);
            mViews.add(view); // 将View添加到mViews列表中
            mRoots.add(root); // 将ViewRootImpl添加到mRoots列表中
            mParams.add(wparams); // 将携带的LayoutParam添加到mParams列表中
            try {
                // 将View设置给RootViewImpl
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
            }
        }
    }

以上方法中,会创建ViewRootImpl对象,然后将view、ViewRootImpl、WindowManager.LayoutParmas对象分别添加到了对应列表中后,并执行ViewRootImpl#setView()方法。

2.3.ViewRootImpl.setView()

// frameworks/base/core/java/android/view/ViewRootImpl.java

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
                ......
		// 注册VSync信号监听器,准备进行layout
		requestLayout();
                try {
                    // 通过WindowSession,将调用进入WMS中
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
                } catch (RemoteException e) {
                }
            }
    }

在ViewRootImpl#setView()方法中,会设置这个view的许多属性,然后执行mWindowSession#addToDisplayAsUser()方法。

mWindowSession在创建ViewRootImpl实例时获得,下面来看下mWindowSession的获取过程。

2.3.获取IWindowSession对象

ViewRootImpl是每一个View的管理者,承载着客户端端和WMS服务端的交互重任(通过IWindowSession对象和IWindow对象),继续来看ViewRoot的构造方法:

// frameworks/base/core/java/android/view/ViewRootImpl.java
    
    public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession(),
                false /* useSfChoreographer */);
    }

这里看到,通过WindowManagerGlobal#getWindowSession()方法,获得了一个IWindowSession接口的对象。ViewRootImpl本身不具有跨进程通信的能力,当它需要向WMS发出交互请求时,就是通过IWindowSession实例来进行。

接着看下WindowManagerGlobal#getWindowSession()方法:

// frameworks/base/core/java/android/view/WindowManagerGlobal.java

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // WMS远端对象
                    IWindowManager windowManager = getWindowManagerService();
                    // 从WMS中获取Session
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                ......
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                }
            }
            return sWindowSession;
        }
    }

在这里,通过WMS打开了一个Session:

// frameworks/base/core/java/android/view/WindowManagerGlobal.java

@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);
}

WMS中则直接返回了一个Session类对象。

由于ViewRootImpl中持有了来自于WMS中的IWindowSession实例,因此在当它需要和WMS进行交互时,将直接通过该实例进行。

3.进入sysmtem_server添加流程

客户端进程通过Session对象,最终通过Binder调用进入了WindowManagerService中,所以接下来的流程将在system_server中执行。

3.1.Session.addToDisplay()

进入Session.addToDisplay()后,将直接调用进入WMS中:

// frameworks/base/services/core/java/com/android/server/wm/Session.java

@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
        Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
            outInsetsState);
}

3.2.WindowManagerService#addWindow()

WMS.addWindow()方法如下:

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
		......
	// 获取Window type
        final int type = attrs.type;

        synchronized (mGlobalLock) {
            ......
            ActivityRecord activity = null;
            // 对于type为SUB_WINDOW的窗口,判断是否存在父窗口
            final boolean hasParent = parentWindow != null;
            // 根据attr.token获取WindowToken对象
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);

            final int rootType = hasParent ? parentWindow.mAttrs.type : type;
            // 如果不存在WindowToken,创建新的WindowToken
            if (token == null) {
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
            // 如果是Application类型的窗口,则该WindowToken向下转型为ActivityRecord
            } else if (rootType >= FIRST_APPLICATION_WINDOW
                    && rootType <= LAST_APPLICATION_WINDOW) {
                activity = token.asActivityRecord();
			.........
            } 
			
            // 创建WindowState对象
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
            // 调整win.mAttrs属性
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
            // 创建InputChanel,用于input事件的传递和接收
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }

            // From now on, no exceptions or errors allowed!
            res = WindowManagerGlobal.ADD_OKAY;
            // 将Windowstate和IWindow添加到mWindowMap中
            mWindowMap.put(client.asBinder(), win);
            // 将WindowToken向下转型
            final ActivityRecord tokenActivity = token.asActivityRecord();
            boolean imMayMove = true;
            // 将WindowState对象添加到WindowToken中,WindowToken将作为WindowState的父容器
            win.mToken.addWindow(win);
            // 窗口动画对象 
            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;
			.....
            // 设置Inset
            outInsetsState.set(win.getInsetsState(), win.isClientLocal());
            // 将InputMonitor#mUpdateInputWindowsNeeded属性设置为true,表示将更新Input内部
            displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();

            boolean focusChanged = false;
            //  如果能收到按键
            if (win.canReceiveKeys()) {
                // 更新焦点窗口
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }
            // 为该WindowState分配layer,但是此次并不会真正地为分配layer
            win.getParent().assignChildLayers();
            // 更新Input Window
            if (focusChanged) {
                displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
                        false /*updateInputWindows*/);
            }
            displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);

            // 如果方向有更新,更新全局配置
            if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
                displayContent.sendNewConfiguration();
            }
            
            getInsetsSourceControls(win, outActiveControls);
        }
        return res;
    }

以上方法中,先进行了大量的验证,如果验证成功,则进入开始做进一步的处理。这里省略去了一些针对特殊窗口的处理,所做工作主要如下:

  • 根据传入的attrs.token,从DisplayContent#mTokenMap<IBinder, WindowToken>中获取WindowToken对象,如果获取不到,则会创建新的WindowToken;
  • 创建WindowState对象;
  • 将WindowState对象添加到WindowToken中,WindowToken将成为WindowState对象的父容器;
  • 创建InputChannel,用于input事件派发;
  • 更新焦点窗口;
  • 更新InputWindow;

下面分别来看这几步操作。

3.3.创建WindowToken对象

WindowToken作为WindowState的父容器,负责管理一组Window。Window添加过程中,WindowToken的创建是视情况而定的,因为一个WindowToken管理着一组Window,如果在添加Window的过程中,已经存在合适的WindowToken,那么就不会创建,否则会创建WindowToken。

WindowToken的构造方法如下:

// frameworks/base/services/core/java/com/android/server/wm/WindowToken.java

    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
            DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
            boolean roundedCornerOverlay, boolean fromClientToken) {
        super(service);
        token = _token;			// IBinder对象,作为一组窗口的实际token
        windowType = type;		// 窗口类型
        mPersistOnEmpty = persistOnEmpty;		// 是否是显式添加的WidowToken
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;		// 是否具有MANAGE_APP_TOKENS权限
        mOwnerUid = ownerUid;			// owner id
        mRoundedCornerOverlay = roundedCornerOverlay;		// 是否覆盖圆角
        mFromClientToken = fromClientToken;
        if (dc != null) {
            dc.addWindowToken(token, this);		// 将该对象添加到DisplayContent中
        }
    }

以上方法中,在对一些属性进行初始化后,将WindowToken和对应的IBinder对象传递给DisplayContent,并以<IBinder, WindowToken>的形式,保存在DisplayContent#mToken中,同时将Token放置在合适的容器中:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

private void addWindowToken(IBinder binder, WindowToken token) {
    // 如果该WindowToken已经关联有DisplayContent对象,则不能再关联其他DisplayContent对象
    final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
    
    // 将WindowToken添加到DisplayContent#mTokenMap中
    mTokenMap.put(binder, token);
    // 对于非Activity类型的窗口,会根据Window类型添加到对应三个container中
    if (token.asActivityRecord() == null) {
		// WindowToken和DisplayContent关联
		token.mDisplayContent = this;

        switch (token.windowType) {
			// 将输入法Window,放在mImeWindowsContainers中
            case TYPE_INPUT_METHOD:
            case TYPE_INPUT_METHOD_DIALOG:
                mImeWindowsContainers.addChild(token);
                break;
            case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
                mOverlayContainers.addChild(token);
                break;
			// 其他类型Window,放在mDisplayAreaPolicy
            default:
                mDisplayAreaPolicy.addWindow(token);
                break;
        }
    }
}

以上方法中,mImeWindowsContainers、mOverlayContainers和mDisplayAreaPolicy将作为WindowToken的父容器对其进行管理,其中:

  • mImeWindowsContainers:ImeContainer类对象,作为IME窗口的父容器,管理IME窗口;
  • mOverlayContainers:NonAppWindowContainers类对象,作为和应用不关联的窗口的父容器,如状态栏等;
  • mDisplayAreaPolicy: DisplayAreaPolicy类对象,它本身并非容器,但该对象作为策略会持有一个合适的容器,这个容器就是DisplayArea.Token, 来管理其他类型的窗口;

这三个容器的类结构如下:

wm1.jpg

此时,WindowToken对象创建完毕。

3.4.创建WindowState对象

在得到WindowToken对象后,接下来将创建WindowState对象,一个WindowState代表了一个具体的Window,每添加一个Window,都会创建对应的WindowState对象。

WindowState类的构造方法如下:

//  frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, int showUserId,
            boolean ownerCanAddInternalSystemWindow, PowerManagerWrapper powerManagerWrapper) {
        super(service);		// 执行WindowContainer(wms)
        mSession = s;		// Session对象
        mClient = c;		// 客户端的IWindow类型对象,该对象和客户端进行交互
        mToken = token;		// WindowToken对象
        mActivityRecord = mToken.asActivityRecord();		// 该token如果是ActivityRecord类型,则相当于向下转型
        mAttrs.copyFrom(a);		// 从传入的attr对象复制给mAttrs对象
        mLastSurfaceInsets.set(mAttrs.surfaceInsets);
        mViewVisibility = viewVisibility;		// 窗口可见性
        mPolicy = mWmService.mPolicy;			// PhoneWindowManager对象
        mContext = mWmService.mContext;			
        mSeq = seq;
        mPowerManagerWrapper = powerManagerWrapper;
        mForceSeamlesslyRotate = token.mRoundedCornerOverlay;	// 圆角窗口的特殊标记,控制圆角的方向旋转
        // 设置Binder die代理
        try {
            c.asBinder().linkToDeath(deathRecipient, 0);
        } catch (RemoteException e) {
        }
        mDeathRecipient = deathRecipient;
	// 对Sub Window类型窗口设置BaseLayer和SubLayer值
        if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
            mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
            mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
            mIsChildWindow = true;
            mLayoutAttached = mAttrs.type !=
                    WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
            mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
                    || parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
            mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
        } else {// 对非子窗口类型窗口设置BaseLayer和SubLayer值
			......
        }
	// 是否为"漂浮"类窗口,因为该俩窗口需要挂在其他窗口上显示
        mIsFloatingLayer = mIsImWindow || mIsWallpaper;
	// 创建WindowStateAnimator对象
        mWinAnimator = new WindowStateAnimator(this);
        mWinAnimator.mAlpha = a.alpha;
	// 初始话宽高
        mRequestedWidth = 0;	
        mRequestedHeight = 0;
        mLastRequestedWidth = 0;
        mLastRequestedHeight = 0;
        mLayer = 0;
	// 创建InputWindowHandle对象
        mInputWindowHandle = new InputWindowHandle(
                mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null,
                    getDisplayId());
	// 如果是子窗口类型窗口,需要添加到父窗口中
        if (mIsChildWindow) {
            parentWindow.addChild(this, sWindowSubLayerComparator);
        }
	// 获取WindowProcessController对象,管理进程
        mWpcForDisplayConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
                ? null
                : service.mAtmService.getProcessController(s.mPid, s.mUid);
    }

在以上方法中,对WindState类中的许多属性进行了初始化操作,其中还创建了两个对象——WindowStateAnimator对象和InputWindowHandle对象。

WindowStateAnimator对象用来为WindowState对象进行动画和Surface的转变操作,绘制过程中,动画的状态和Surface状态都会由该对象来进行记录。

InputWindowHandle对象则用于input事件的分发,通过内部的IBinder类型的token,将InputChannel和WindowState进行关联。这俩对象的创建相对较简单,这里就略去了。

3.5.将WindowToken设置为WindoState的父容器

接下来,会通过WindowToken#addWindow()方法,将WindowState对象添加到WindowToken中,使得WindowToken成为WindowState的父容器:

// frameworks/base/services/core/java/com/android/server/wm/WindowToken.java

    void addWindow(final WindowState win) {
	// 对于子窗口而言,由父窗口管理,不需要由WindowToken管理
        if (win.isChildWindow()) {
            return;
        }
	// 如果mSurfaceControl不存在创建createSurfaceControl对象
        if (mSurfaceControl == null) {
            createSurfaceControl(true /* force */);
        }
	// 添加到mChildren列表中,并设置WindowStack#mParent为该WindowToken
        if (!mChildren.contains(win)) {
            addChild(win, mWindowComparator);
            mWmService.mWindowsChanged = true;
        }
    }

以上方法中,通过addChild()方法,将WindowState对象添加到WindowToken#mChildren列表中,同时将WindowToken赋值给WindowState#mParent属性。

3.6.创建InputChannel

InputChannel用于传输input事件给应用,其内部通过socket的方式,将inputFlinger中的input事件读取并传递给应用,在WindowState#的方式创建并注册了InputChannel对象:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    void openInputChannel(InputChannel outInputChannel) {
        
        String name = getName();
        // 创建一个InputChannel对
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        // 向IMS中注册InputChannel
        mWmService.mInputManager.registerInputChannel(mInputChannel);
        mInputWindowHandle.token = mInputChannel.getToken();
        // 填充返回给ViewRootImpl的outInputChannel
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } 
        ......
        mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
    }

这个方法中,首先获取InputChannel对,然后向IMS中注册InputChannel,最后将inputChannels[1]返回给ViewRootImpl。 关于InputChannel传递input事件部分,会在Input模块中进行详细说明。

3.7.更新焦点窗口

如果该WindowState能接收key事件,则接下来会通过WMS#updateFocusedWindowLocked()方法更新全局的焦点窗口。在更新时,将会自顶向下,从RootWindowContainer → DisplayContent 对每个WindowState进行遍历,最终获取到一个获得焦点的WindowState对象。

更新焦点窗口的流程比较长,对这部分流程进行了单独的分析,见 Android R WindowManagerService模块(5) 焦点窗口和InputWindows的更新

3.8.更新InputWindow

“InputWindow”是指能够接收input事件的窗口。在更新焦点窗口前,就通过InputMonitor#setUpdateInputWindowsNeededLw()方法,将InputMonitor#mUpdateInputWindowsNeeded属性设置为true,表示接下来需要更新Input窗口。

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

    void updateInputWindowsLw(boolean force) {
        if (!force && !mUpdateInputWindowsNeeded) {
            return;
        }
        scheduleUpdateInputWindows();
    }

从Android Q开始,在SurfaceFlinger中通过Binder调用更新InputWindow给InputFlinger。这个过程也比较长,也做了单独分析,见 Android R WindowManagerService模块(5) 焦点窗口和InputWindows的更新

到此为止,整个Window添加过程执行完成。

整个过程时序图如下:

add_window.jpg

4.总结

  1. 添加窗口时,客户端拿到的WindowManager对象是一个WindowManagerImpl实例,且没个Activity和Window都各自的WindowManagerImpl实例,并且和Context绑定。相比WindowManagerImpl对象,每个进程则只有一个WindowManagerGlobal对象;
  2. ViewRootImpl作为每个View的管理者,和WMS进行交互,两进程交互过程通过IWindowSession和IWindow接口进行;
  3. WMS中,WindowState对象代表一个窗口,WindowToken作为WindowState的父容器,管理着一组窗口。

通过WMS#addWindow()方法,创建了相对于system_server的窗口管理对象,但需要显示窗口内容的Surface还没有创建。

在addView()过程中,有一个requestLayout()操作,它监听Vsync信号,并在收到Vsync信号后,请求WMS进行relayout操作,从而开始执行布局过程。下一篇文章将对布局的过程进行分析。