WindowManagerService的理解

Android中的窗口概念:屏幕上一块用于绘制各种UI元素并可以响应用户输入的一块矩形区域。从原理上来看,窗口的概念是独自占有一个Surface实例的显示区域。如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗口。 
上述对窗口的概念性描述,提取出来主要有以下几点信息:

  • 用于绘制UI元素

  • 响应用户输入

  • 独自占有一个surface实例

    • Surface是一块画布,应用通过canvas或者openGL在上面作画
    • 作画后通过SurfaceFlinger将多块Surface的内容按照Z-order进行混合并输出到FrameBuffer,从而将Android的页面显示给用户。

每个窗口都有一块Surface用于显示自己的ui,必然需要一个角色对窗口进行统一管理,这个时候,WMS应运而生。WMS为所有窗口分配Surface,掌管z-order以及位置、尺寸、窗口的进场出场动画,并且还是输入系统的中转站。本文的分析角度:

  • 布局系统:计算管理窗口的位置和层次
  • 动画系统:根据布局系统的计算渲染窗口动画

一个窗口的创建流程大概如下: 
1. 客户端通过WindowManagerGlobal获取IWindowSession,通过IWindowSession来向WMS发起添加请求。 
2. 初始化WindowManager.LayoutParam,指定响应的type,当type大于System级别的时候,不需要token,WMS会创建一个token,而type小于system级别的窗口,是必须要求有token带过来给WMS的。WMS根据这个type来对窗口的z-order进行排序。 
3. 向was申请relayout,所谓重新布局,是根据窗口新的属性去调整其Surface的相关属性,或者重新创建一个Surface。向wms添加一个窗口,仅仅是将它在wms中进行注册。只有经过了relayout后,窗口才拥有了wms为其分配的画布,有了画布,窗口才能进行绘制工作。 
4. 之后,窗口的绘制过程如下: 
– 通过surface.lock()函数获取可以在surface上作画的canvas 
– 使用canvas进行绘图 
– 通过unlockCanvasAndPost提交绘制结果

说到底,Window的本质其实就是一块Surface画布. 
于是,根据对Surface的操作类型可以将Android的显示系统分为3个层次。 
1. UI框架层,负责View空间的布局、绘制、事件分发、响应 
2. WMS管理窗口Surface的布局与次序 
3. SurfaceFlinger 将WMS维护的窗口Surface按照一定的次序混合后显示到屏幕上。

说了那么多,我们来看看窗口的核心管理WMS的构成。 
– WMS也属于系统服务,都是由SystemServer来启动,代码位于SystemServer.java中:

//这里需要加入SystemServer启动WMS的相关代码,最后进入到WMS.Main函数中
  • 1
//可以看到,SystemServer启动WMS的过程是将WSM的构造函数所需要的相关参数传入main方法
    //由main方法来调用HandlerThread的handler将构造函数丢入looper中进行wms的实例化
    public static WindowManagerService main(final Context context,
            final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs,
            final boolean onlyCore) {
        final WindowManagerService[] holder = new WindowManagerService[1];
        //那么这里的runWithScissor到底起了什么作用呢?
        DisplayThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run() {
                holder[0] = new WindowManagerService(context, im,
                        haveInputMethods, showBootMsgs, onlyCore);
            }
        }, 0);
        return holder[0];
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
//这个方法位于android.os.Handler.java类,是一个@hide方法
//同时这个方法是一个阻塞的方法,会阻塞目前县城直到返回,这也就
//说,return的时候在wms线程会被此方法阻塞,直到wms完成实例化
//,也可以理解为,main方法return后,holder可能仍然为null,
//但是wms线程中,所有消息都需要等待这个实例化完成
public final boolean runWithScissors(final 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");
        }

        //由于 wms 的 Main()方法调用
        //如果调用此方法的looper和handler的looper相同,则直接运行返回
        if (Looper.myLooper() == mLooper) {
            r.run();
            return true;
        }

        //如果不是则新建一个阻塞的runnable来进入msgqueue里面
        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

接下来,看wms的构造函数,所有参数都是SystemServer传入:

 private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
        mContext = context;
        mHaveInputMethods = haveInputMethods;
        mAllowBootMessages = showBootMsgs;
        mOnlyCore = onlyCore;
        ...
        //保存了用于接受输入事件的inputManager
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        ...
        //保存屏幕相关信息
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        mDisplays = mDisplayManager.getDisplays();
        for (Display display : mDisplays) {
            createDisplayContentLocked(display);
        }
        ...
        //掌管所有window的动画
        mAnimator = new WindowAnimator(this);

         //初始化WindowManagerPolicy成员
        initPolicy();

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);

        SurfaceControl.openTransaction();
        try {
            createWatermarkInTransaction();
        } finally {
            SurfaceControl.closeTransaction();
        }

        showEmulatorDisplayOverlayIfNeeded();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

至此,WMS的初始化工作已经完成,那么初始化工作有哪些重要的成员变量被初始化呢?

  • mInputManager : 输入系统服务,用于管理每个窗口的输入事件管道(InputManager)以及向通道上派发事件。
  • mChoreographer ,从显示子系统获取VSYNC同步事件,从而来在合适的实际通知渲染动作,避免在渲染的过程中因为发生屏幕重绘而导致画面撕裂。WMS使用这个对象驱动所有的窗口动画,屏幕旋转动画,墙纸动画的渲染。
  • mAnimator,WindowAnimator实例,在Choreographer的驱动下逐个渲染所有动画。
  • mPolicy,WindowManagerPolicy的实现,目前只有PhoneWindowManager一个实现类。mPolicy定义了很多窗口相关的策略,是wms的首席顾问,当WMS要做任何事情的时候,都会向policy求助。例如,过塑wms某一个类型的window 的zOrder是多少,帮助wms矫正不合理的窗口属性,为wms监听屏幕旋转状态。预处理一些系统按键事件,例如(home back键都在这里实现默认行为),所以mPolicy是一个非常重要的成员变量。
  • mDisplayContents ,一个DisplayContent列表,4.2支持基于Wi-Fi Display的多屏幕输出,而一个DisplayContent描述了一块可以绘制窗口的屏幕。每个DisplayContent都用一个整形变量做为其ID。
  • mTokenMap,一个hash map,保存了所有显示令牌(类型为WindowToken),用于窗口管理。一个窗口必须有一个显示令牌,mAppTokens保存了所有的Activity令牌(WindowToken的子类AppWindowToken),mExitingTokens则保存了正在退出过程中的显示令牌,其中mAppTokens列表是有序的,他与AMS中的mHistory的顺序保持一致,反应了系统中activity的顺序。
  • mWindowMap,也是一个HashMap,保存了所有窗口的状态信息,用于窗口管理。状态信息的类型为WindowState。实际上,所有被add过的window的状态都会保存在这个map中,与mTokenMap一样,mWindowMap也有衍生的子集,mPendingRemove保存了退出动画播放完成即将被移除的窗口,mLosingFocus保存了失去输入焦点的窗口。在DisplayContent中,也有一个window列表,这个列表以order为顺序存储了所有window。
  • mSessions,一个List,元素为Session,这个列表保存了所有当前向WMS发起请求的客户端进程唯一的IWindowSession对象,注意,这里的Session也是进程唯一的。
  • mRotation,是一个int变量。保存了当前手机的旋转状态。

接下来,我们从WindowManagerService的addWindow()函数开始讨论WMS的工作流程:


    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];

        //首先,检查权限
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }

        boolean reportNewConfig = false;

        //当为某个窗口添加子窗口的时候,attachedWindow用来保存父窗口的实例
        WindowState attachedWindow = null;
        long origId;
        final int type = attrs.type;

        synchronized(mWindowMap) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }

            // 获取窗口要添加的displayContent
            // 在添加窗口时,必须的通过displayId参数指定添加哪个DisplayContent,如果没有指定,session会替Window添加DefaultDisplay
            // 也就是手机屏幕
            final DisplayContent displayContent = getDisplayContentLocked(displayId);
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ". Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if (!displayContent.hasAccess(session.mUid)) {
                Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                        + "does not have access: " + displayId + ". Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            //已经添加了窗口
            if (mWindowMap.containsKey(client.asBinder())) {
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

            // 如果是type是子窗口
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { //检查添加窗口的父窗口是否为null attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { Slog.w(TAG_WM, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; }

                //如果父窗口还是一个子窗口
                if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; }
            }

            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting."); return WindowManagerGlobal.ADD_PERMISSION_DENIED; }


            boolean addToken = false;

            //从tokenMap中取出已经注册的token
            WindowToken token = mTokenMap.get(attrs.token);
            AppWindowToken atoken = null;
            //如果看看已经注册的token没有
            if (token == null) {
                //如果是application 级别 activity
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG_WM, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
                if (type == TYPE_INPUT_METHOD) { Slog.w(TAG_WM, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
                if (type == TYPE_VOICE_INTERACTION) { Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
                if (type == TYPE_WALLPAPER) { Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
                if (type == TYPE_DREAM) { Slog.w(TAG_WM, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
                if (type == TYPE_QS_DIALOG) { Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
                if (type == TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }

                //如果说以上的情况,都new一个token,最后一个参数是false,表明是wms创建的token
                token = new WindowToken(this, attrs.token, -1, false);
                //说明token是临时创建的
                addToken = true;

                //如果说token不为null,则说明已经被注册过了 ,看一下activity级别的讨论,这里要求级别也是application的
                //并且atoken是appWindowToken
            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { atoken = token.appWindowToken; if (atoken == null) { Slog.w(TAG_WM, "Attempted to add window with non-application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { Slog.w(TAG_WM, "Attempted to add window with exiting application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_APP_EXITING; }
                if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v( TAG_WM, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; }
            } else if (type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { Slog.w(TAG_WM, "Attempted to add input method window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
            } else if (type == TYPE_VOICE_INTERACTION) { if (token.windowType != TYPE_VOICE_INTERACTION) { Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
            } else if (type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
            } else if (type == TYPE_DREAM) { if (token.windowType != TYPE_DREAM) { Slog.w(TAG_WM, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
            } else if (type == TYPE_ACCESSIBILITY_OVERLAY) { if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
            } else if (type == TYPE_QS_DIALOG) { if (token.windowType != TYPE_QS_DIALOG) { Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; }
            } else if (token.appWindowToken != null) {
                Slog.w(TAG_WM, "Non-null appWindowToken for system window of type=" + type);
                // It is not valid to use an app token with other system types; we will
                // instead make a new token for it (as if null had been passed in for the token).
                attrs.token = null;
                token = new WindowToken(this, null, -1, false);
                addToken = true;
            }

            //这个windowState维护了所有窗口的信息
            WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            //windowPolicy粉墨登场
            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }

            // From now on, no exceptions or errors allowed!

            res = WindowManagerGlobal.ADD_OKAY;

            if (excludeWindowTypeFromTapOutTask(type)) { displayContent.mTapExcludedWindows.add(win); }

            origId = Binder.clearCallingIdentity();

            //刚才被wms临时创建的token被加入到tokenMap中
            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
            win.attach();

            //然后将WindowState加入到map中
            mWindowMap.put(client.asBinder(), win);
            if (win.mAppOp != AppOpsManager.OP_NONE) {
                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
                    win.setAppOpVisibilityLw(false);
                }
            }

            if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + token.appWindowToken + " startingWindow=" + win); }

            boolean imMayMove = true;

            if (type == TYPE_INPUT_METHOD) { win.mGivenInsetsPending = true; mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (type == TYPE_WALLPAPER) { mWallpaperControllerLocked.clearLastWallpaperTimeoutTime(); displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if (mWallpaperControllerLocked.isBelowWallpaperTarget(win)) { // If there is currently a wallpaper being shown, and // the base layer of the new window is below the current // layer of the target window, then adjust the wallpaper. // This is to avoid a new window being placed between the // wallpaper and its target. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; }
            }

            // If the window is being added to a task that's docked but non-resizeable,
            // we need to update this new window's scroll position when it's added.
            win.applyScrollIfNeeded();

            // If the window is being added to a stack that's currently adjusted for IME,
            // make sure to apply the same adjust to this new window.
            win.applyAdjustForImeIfNeeded();

            if (type == TYPE_DOCK_DIVIDER) { getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win); }

            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;
            // Check if we need to prepare a transition for replacing window first.
            if (atoken != null && !prepareWindowReplacementTransition(atoken)) {
                // If not, check if need to set up a dummy transition during display freeze
                // so that the unfreeze wait for the apps to draw. This might be needed if
                // the app is relaunching.
                prepareNoneTransitionForRelaunching(atoken);
            }

            if (displayContent.isDefaultDisplay) {
                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                final Rect taskBounds;
                if (atoken != null && atoken.mTask != null) {
                    taskBounds = mTmpRect;
                    atoken.mTask.getBounds(mTmpRect);
                } else {
                    taskBounds = null;
                }
                if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, mRotation,
                        displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
                        outStableInsets, outOutsets)) {
                    res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
                }
            } else {
                outContentInsets.setEmpty();
                outStableInsets.setEmpty();
            }

            if (mInTouchMode) {
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
            }
            if (win.mAppToken == null || !win.mAppToken.clientHidden) {
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
            }

            mInputMonitor.setUpdateInputWindowsNeededLw();

            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                moveInputMethodWindowsIfNeededLocked(false);
            }

            mLayersController.assignLayersLocked(displayContent.getWindowList());
            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.

            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);

            if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));

            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                reportNewConfig = true;
            }
            if (attrs.removeTimeoutMilliseconds > 0) {
                mH.sendMessageDelayed(
                        mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win),
                        attrs.removeTimeoutMilliseconds);
            }
        }

        if (reportNewConfig) {
            sendNewConfiguration();
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371

接下来,我们来理解WindowToken的重要作用:

WindowToken类位于:com.android.server.wm.WindowToken.java,

/** * Container of a set of related windows in the window manager. Often this * is an AppWindowToken, which is the handle for an Activity that it uses * to display windows. For nested windows, there is a WindowToken created for * the parent window to manage its children. */
//先看类说明,WindowToken是一个位于WindowManager中相互关联的Window的结合。
//对于一个activity来说,WindowToken是一个AppWindowToken的句柄(WindowToken的一个子类),用来显示各种window。对于嵌套的window来说,会创建一个WindowToken来给父window管理儿子。
class WindowToken {
    // The window manager!
    final WindowManagerService service;

    // The actual token.
    final IBinder token;

    // The type of window this token is for, as per WindowManager.LayoutParams.
    final int windowType;

    // Set if this token was explicitly added by a client, so should
    // not be removed when all windows are removed.
    final boolean explicit;

    // For printing.
    String stringName;

    // If this is an AppWindowToken, this is non-null.
    AppWindowToken appWindowToken;

    // All of the windows associated with this token.
    final WindowList windows = new WindowList();

    // Is key dispatching paused for this token?
    boolean paused = false;

    // Should this token's windows be hidden?
    boolean hidden;

    // Temporary for finding which tokens no longer have visible windows.
    boolean hasVisible;

    // Set to true when this token is in a pending transaction where it
    // will be shown.
    boolean waitingToShow;

    // Set to true when this token is in a pending transaction where its
    // windows will be put to the bottom of the list.
    boolean sendingToBottom;

    WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) {
        service = _service;
        token = _token;
        windowType = type;
        explicit = _explicit;
    }

    void removeAllWindows() {
        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
            WindowState win = windows.get(winNdx);
            if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM, "removeAllWindows: removing win=" + win);
            win.mService.removeWindowLocked(win);
        }
        windows.clear();
    }

    void dump(PrintWriter pw, String prefix) {
        pw.print(prefix); pw.print("windows="); pw.println(windows);
        pw.print(prefix); pw.print("windowType="); pw.print(windowType);
                pw.print(" hidden="); pw.print(hidden);
                pw.print(" hasVisible="); pw.println(hasVisible);
        if (waitingToShow || sendingToBottom) {
            pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
                    pw.print(" sendingToBottom="); pw.print(sendingToBottom);
        }
    }

    @Override
    public String toString() {
        if (stringName == null) {
            StringBuilder sb = new StringBuilder();
            sb.append("WindowToken{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(" "); sb.append(token); sb.append('}');
            stringName = sb.toString();
        }
        return stringName;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • WindowToken将同一个应用组件的窗口组织在一起,应用组件可以是Activity,InputMethod,WallPaper以及Dream。在WMS管理窗口的过程中,用WindowToken指代一个应用组件。例如在进行窗口ZOrder的排序中,属于同一个WindowToken的窗口会安排在一起。在其中定义的一些属性会影响到所有属于这个windowToken的窗口。
  • WindowToken具有令牌的作用,这是WMS管理应用组件的一个手段。WindowToken由应用组件或其管理者负责向WMS生命并持有。应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken类型一致。从前面代码看到,System级别窗口不需要提供Token,会由WMS隐式的创建一个WindowToken。那么是不是所有人都能够添加一个系统窗口呢?可以看到addWindow刚开始mPolicy就对应用进行了permission的检查,也就是客户端持有SYSTEM_ALERT_WINDOW等呼出系统窗口的权限,才能创建系统窗口。

那么,应用组件在创建一个窗口的时候必须指定的WindowToken,到底该如何声明呢? 
回到WMS的addWindowToken()函数中,

//可以看到,token其实是一个Binder对象的包装
  @Override
    public void addWindowToken(IBinder token, int type) {
        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                "addWindowToken()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }

        synchronized(mWindowMap) {
        //去map中找token,以binder对象为键,WindowToken为值
            WindowToken wtoken = mTokenMap.get(token);
            if (wtoken != null) {
                Slog.w(TAG_WM, "Attempted to add existing input method token: " + token);
                return;
            }
            //创建一个windowToken,放入map中
            wtoken = new WindowToken(this, token, type, true);
            mTokenMap.put(token, wtoken);
            if (type == TYPE_WALLPAPER) {
                mWallpaperControllerLocked.addWallpaperToken(wtoken);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

从addWindowToken()这个函数可以看到,WindowToken有两层含义。 
– 对显示组件(客户端)而言,token是一个任意的binder实例,对客户端而言,token仅仅是一个创建窗口的令牌,没有其他含义。 
– 对WMS来说,WindowToken是一个WindowToken的实例,包装了客户端一侧的binder实例,并且以这个token为键,以WindowToken为值,保存了token。mTokenMap是否已经保存了该binder实例和对应的WindowToken,标志着客户端一侧的binder token是否已经在WMS声明。

  • WMS通过token来管理各个窗口,那么最重要的Activity的token是如何管理的呢,首先Activity的token使用方式与WallPaper和InputMethod类似的,但是扩展了更多的内容。对此,WMS创建了一个继承WindowToken的AppWindowToken来对Activity进行描述和管理。既然AppWindowToken是为Activity服务的,那么其声明也自然是在ActivityManagerService中,具体位置在ActivityStack.validateAppTokensLocked()中,代码如下:
 final void validateAppTokensLocked() {
        mValidateAppTokens.clear();
        mValidateAppTokens.ensureCapacity(numActivities());
        final int numTasks = mTaskHistory.size();
        for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
            TaskRecord task = mTaskHistory.get(taskNdx);
            final ArrayList<ActivityRecord> activities = task.mActivities;
            if (activities.isEmpty()) {
                continue;
            }
            TaskGroup group = new TaskGroup();
            group.taskId = task.taskId;
            mValidateAppTokens.add(group);
            final int numActivities = activities.size();
            for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
                final ActivityRecord r = activities.get(activityNdx);
                group.tokens.add(r.appToken);
            }
        }
//WMS对appTokens进行更新
        mWindowManager.validateAppTokens(mStackId, mValidateAppTokens);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

下面一段代码是AppToken的aidl类,可以看出,实际的binder对象是由这个AIDL文件生成的stub类,分别有一些方法供给WMS进行回调

interface IApplicationToken {
//窗口完成初次绘制
    void windowsDrawn();
    //窗口可见通知AMS
    void windowsVisible();
    //窗口不可见通知AMS
    void windowsGone();
    //窗口没能按时完成输入事件的处理
    boolean keyDispatchingTimedOut(String reason);
    //从AMS处获取界定ANR的事件
    long getKeyDispatchingTimeout();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

AMS通过ActivityRecord表示一个Activity,而ActivityRecord的appToken在其构造函数时被创建,类型为IApplicationToken.Stub,就是上述AIDL的stub类,所以每个Activity有自己的appToken,而WMS接受AMS对Application的Binder对象appToken粘结了AMS的ActivityRecord与WMS的AppWindowToken,只要给定一个ActivityRecord,都可以通过appToken在WMS中找到对应的AppWindowToken,从而使得AMS拥有了操纵Activity窗口绘制的能力。例如,当AMS认为一个Activity需要被隐藏的时候,以Activity对应的ActivityRecord所拥有的appToken为参数调用WMS的setAppVisibility()函数,此函数通过appToken找到appdWindowToken(通过mAppWindowMap),然后将这个token的窗口隐藏。

  • 理解WindowState:

从WindowManagerService.addWindow()函数的实现看到,当WMS添加一个窗口时,WMS会为其创建一个WindowState。WindowState表示一个窗口的所有实行,所以他是WMS中事实上的窗口。 
类似于WindowToken,WindowState在显示组件一侧也有对应的类型:IWindow.Stub 。这个借口提供了窗口管理相关的通知回调,如尺寸的变化、焦点的变化等。WindowState被保存到mWindowMap中,键值为IWindow的Bp端。mWindowMap是整个系统所有窗口的一个全集

    原文作者:kaijiehui
    原文地址: https://blog.csdn.net/u011341111/article/details/79458147
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞