当前位置: 首页>后端>正文

Android WindowManagerService--04:Window的添加过程

本文转载自:

  • Android R WindowManagerService模块(2) Window的添加过程
  • Android解析WindowManager(三)Window的添加过程

本文基于Android 11.0源码分析

前言

??窗口的添加,站在用户角度看,是开启一个新界面;站在开发者角度看,是通过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:

//frameworks/base/core/java/android/view/WindowManager.java
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的添加过程

??三大类窗口的添加过程会有所不同,这里以系统窗口StatusBar为例,StatusBar是SystemUI的重要组成部分,具体就是指系统状态栏,用于显示时间、电量和信号等信息。我们来查看StatusBar的实现类PhoneStatusBar的addStatusBarWindow方法,这个方法负责为StatusBar添加Window。下面我们从PhoneStatusBar.addStatusBarWindow()方法开始,对添加窗口过程进行分析。

2.1 PhoneStatusBar.addStatusBarWindow()

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
 private void addStatusBarWindow() {
    makeStatusBarView();//1.构建视图
    mStatusBarWindowManager = new StatusBarWindowManager(mContext);
    mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
            mHeadsUpManager);
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//2
}

注释1处用于构建StatusBar的视图。在注释2处调用了StatusBarWindowManager的add方法,并将StatusBar的视图(StatusBarWindowView)和StatusBar的传进去。

//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
public void add(View statusBarView, int barHeight) {
    mLp = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            barHeight,
            WindowManager.LayoutParams.TYPE_STATUS_BAR,//1.窗口类型
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
    mLp.gravity = Gravity.TOP;
    mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
    mLp.setTitle("StatusBar");
    mLp.packageName = mContext.getPackageName();
    mStatusBarView = statusBarView;
    mBarHeight = barHeight;
    mWindowManager.addView(mStatusBarView, mLp);//2.添加窗口
    mLpChanged = new WindowManager.LayoutParams();
    mLpChanged.copyFrom(mLp);
}

首先通过创建LayoutParams来配置StatusBar视图的属性,包括Width、Height、Type、 Flag、Gravity、SoftInputMode等。 关键在注释1处,设置了TYPE_STATUS_BAR,表示StatusBar视图的窗口类型是状态栏。在注释2处调用了WindowManager的addView方法,addView方法定义在WindowManager的父类接口ViewManager中,而实现addView方法的则是WindowManagerImpl中。

2.2 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.3 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()方法。ViewRootImpl身负了很多职责:

  • View树的根并管理View树;

  • 触发View的测量、布局和绘制;

  • 输入事件的中转站;

  • 管理Surface;

  • 负责与WMS进行进程间通信。

2.4 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.5 获取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/services/core/java/com/android/server/wm/WindowManagerService.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
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, 来管理其他类型的窗口。

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

Android WindowManagerService--04:Window的添加过程,第1张
WMS08.png

此时,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的父容器

??在3.2 WindowManagerService#addWindow()小节中,会通过WindowToken#addWindow()方法,将WindowState对象添加到WindowToken中,使得WindowToken成为WindowState的父容器:

//3.2 WindowManagerService#addWindow()
win.mToken.addWindow(win);
// 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。

3.7 更新焦点窗口

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

??更新焦点窗口的流程比较长,对这部分流程后续进行单独的分析。

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。

??到此为止,整个Window添加过程执行完成。整个过程时序图如下:

Android WindowManagerService--04:Window的添加过程,第2张
WMS09.png

4.Activity的添加窗口过程

??无论是哪种窗口,它的的添加过程在WMS处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面会有些不同。但是在WindowManager处理部分会有所不同,这里以最典型的应用程序窗口Activity为例,Activity在启动过程中,如果Activity所在的进程不存在则会创建新的进程,创建新的进程之后就会运行代表主线程的实例ActivityThread。当界面要与用户进行交互时,会调用ActivityThread的handleResumeActivity方法,如下所示。

//frameworks/base/core/java/android/app/ActivityThread.java
 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
       ...   
         r = performResumeActivity(token, clearHide, reason);//1.最终调用Activity的onResume       
  ...
     if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();//2
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//3
                }
...                
}

注释1处的performResumeActivity方法最终会调用Activity的onResume方法。在注释2处得到ViewManager类型的wm对象,在注释3处调用了wm的addView方法,而addView方法的实现则是在WindowManagerImpl中,此后的过程在上面的系统窗口的添加过程已经讲过,唯一需要注意的是addView的第一个参数是DecorView。

5.总结

  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操作,从而开始执行布局过程。后面文章将对布局的过程进行分析。


https://www.xamrdz.com/backend/3t51940154.html

相关文章: