WindowManagerService之addWindow篇

一:背景

    应用启动,在执行完Activity的onCreate/onStart/onResume生命周期函数后,会调用wm.addView来像WMS添加窗口并绘制自己的视图。下面接着wm.addView来看下应用窗口是如何添加的。前置流程可查看应用冷启动流程分析 - AndroidU_notifyactivitylaunching-优快云博客的4.2章节。

二:应用窗口添加

2.1 ViewManager.addView

    在ActivityThread的handleResumeActivity函数中,调用完Activity的onResume后,会调用ViewManager的addView函数来触发布局的加载和显示

//frameworks/base/core/java/android/app/ActivityThread.java
    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
        ...
        ViewManager wm = a.getWindowManager();
        //调用WindowManagerImpl的addView函数
        wm.addView(decor, l);
        ...
    }

这里看下wm的赋值流程,通过下面代码可以看到wm是WindowManagerImpl即WindowManager的实现类

//frameworks/base/core/java/android/app/Activity.java
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

//frameworks/base/core/java/android/app/Activity.java
    final void attach(...) {
        ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...
        mWindowManager = mWindow.getWindowManager();
        ...
    }

//frameworks/base/core/java/android/view/Window.java
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

//frameworks/base/core/java/android/view/Window.java
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        ...
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

//frameworks/base/core/java/android/view/WindowManagerImpl.java
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
    }

2.2 WindowManagerGlobal.addView

  • view重复性判定,不允许view重复添加
  • 创建ViewRootImpl对象
  • 更新mViews/mRoots/mParams数组

    在WindowManagerGlobal中有三个数组非常重要。mViews/mRoots/mParams分别保存View、ViewRootImpl、LayoutParams,相同下标的View、ViewRootImpl、LayoutParams对象是一一对应关系

//frameworks/base/core/java/android/view/WindowManagerImpl.java
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyTokens(params);
        //调用WindowManagerGlobal的addView函数。mContext.getDisplayNoVerify()会创建一个默认的Display作为形参传入
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

//frameworks/base/core/java/android/view/WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        //参数合法性判断
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ...
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...
            //在mViews列表中查找当前view,如果找到则返回view对应下标
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    ...
                } else {//view不允许重复添加,抛异常中断addview流程
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
            }
            ...
            IWindowSession windowlessSession = null;
            ...
            if (windowlessSession == null) {
                //创建ViewRootImpl对象
                root = new ViewRootImpl(view.getContext(), display);
            } else {
                ...
            }

            view.setLayoutParams(wparams);

            //更新数组。mViews/mRoots/mParams分别保存View、ViewRootImpl、LayoutParams,相同下标的View、ViewRootImpl、LayoutParams对象是一一对应关系
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            try {
                //调用ViewRootImpl的setView函数
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                ...
            }
        }
    }

2.3 ViewRootImpl.setView

  • 调用requestLayout函数触发重新布局
  • 初始化inputChannel,保证view可以收到input事件
  • 调用Session的addToDisplayAsUser函数继续窗口添加流程
//frameworks/base/core/java/android/view/ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...
                //把当前view加入到FallbackEventHandler中。FallbackEventHandler Android 系统中一个用于处理未消费的输入事件的备用机制。它主要用于处理那些没有被应用程序的视图层次结构(View Hierarchy)消费的事件。
                mFallbackEventHandler.setView(view);
                //把attrs赋值给mWindowAttributes
                mWindowAttributes.copyFrom(attrs);
                if (mWindowAttributes.packageName == null) {
                    mWindowAttributes.packageName = mBasePackageName;
                }
                //把mWindowAttributes赋值给attrs
                attrs = mWindowAttributes;
                ...
                //触发重新布局
                requestLayout();
                //初始化inputChannel,保证view可以收到input事件
                InputChannel inputChannel = null;
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    inputChannel = new InputChannel();
                }
                ...
                try {
                    ...
                    //mWindow是IWindow类型的匿名Binder服务,用来让WMS主动和当前窗口通信用的
                    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,
                            mTempControls, attachedFrame, compatScale);
                    ...
                }
                ...
            }
        }
    }

这里介绍下mWindowSession和mWindow的创建过程

mWindowSession的创建过程:通过WMS的openSession函数new了一个Session对象。每个应用程序进程通过 Session 与 WMS 通信

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

//frameworks/base/core/java/android/view/WindowManagerGlobal.java
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    ...
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

//frameworks/base/services/core/java/com/android/internal/wm/WindowManagerService.java
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
    }

mWindow的创建过程:new了一个W对象。mWindow是客户端提供的一个匿名binder,用于WMS和客户端通信

//frameworks/base/core/java/android/view/ViewRootImpl.java
    public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
            WindowLayout windowLayout) {
        ...
        mWindow = new W(this);
        ...
    }

//frameworks/base/core/java/android/view/ViewRootImpl.java
    static class W extends IWindow.Stub implements WindowStateTransactionItem.TransactionListener {
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;
        ...

        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
        }
    }

2.4 ViewRootImpl.requestLayout

  • 检查是否是主线程,只有主线程可以出发requestLayout
  • 向Choreographer添加一个Choreographer.CALLBACK_TRAVERSAL类型的回调,当Choreographer收到Vsync信号时,会触发该回调,从而触发mTraversalRunnable线程开始运行

    具体流程后续会有专门的文章介绍...

//frameworks/base/core/java/android/view/ViewRootImpl.java
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            //检查线程合法性,判断当前线程是否是创建view的线程即是否是主线程
            checkThread();
            mLayoutRequested = true;
            //触发遍历view树
            scheduleTraversals();
        }
    }

//frameworks/base/core/java/android/view/ViewRootImpl.java
    void scheduleTraversals() {
        ...
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            ...
            //在MessageQueue中插入一个同步屏障,确保Choreographer的vsync信号优先处理。同步屏障会阻塞MessageQueue中的普通消息的的处理,但允许异步消息继续执行
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            ...
            //通过Choreographer将UI刷新任务和系统的Vsync信号同步,确保UI更新的流畅性和一致性。当Choreographer收到Vsync信号时,会触发CALLBACK_TRAVERSAL回调,启动mTraversalRunnable线程
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            //通知Renderthread Vsync信号即将到来
            notifyRendererOfFramePending();
            ...
        }
    }

2.5 WindowManagerService.addWindow

  • 获取客户端对应的WindowToken
  • 创建WindowState对象
  • 把新添加的窗口对应的WindowState加入到Session的mAddedWindows数组中
  • 把客户端对应的binder和对应的WindowState作为键值对加入到mWindowMap数组中
  • 窗口挂载。把WindowState加入到WindowToken中,其实是加入到WindowToken的父类WindowContainer的mChildren中
//frameworks/base/services/core/java/com/android/internal/wm/Session.java
    public int addToDisplayAsUser(...) {
        return mService.addWindow(...);
    }

//frameworks/base/services/core/java/com/android/internal/wm/WindowManagerService.java
    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
            float[] outSizeCompatScale) {
        ...
        synchronized (mGlobalLock) {
            if (!mDisplayReady) {//Display没有初始化好,抛异常中断流程
                throw new IllegalStateException("Display has not been initialialized");
            }
            if (session.isClientDead()) {//客户端死亡,例如应用crash了,抛异常中断流程
                ...
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
            //先获取windowToken对应的DisplayContent,如果获取不到则根据displayId获取对应的DisplayContent
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
            ...
            //限制子窗口必须依赖于父窗口且父窗口不能是子窗口类型
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                parentWindow = windowForClientLocked(null, attrs.token, false);
                if (parentWindow == null) {
                    ...
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    ...
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }
            ...
            //获取客户端对应的WindowToken。每一个Activity都是一个WindowToken,一个Activity对应一个ActivityRecord,ActivityRecord继承WindowToken
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            //获取Window类型
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;
            ...
            if (token == null) {
                ...
            } else if (rootType >= FIRST_APPLICATION_WINDOW
                    && rootType <= LAST_APPLICATION_WINDOW) {//应用类型窗口,做合法性检查
                activity = token.asActivityRecord();
                if (activity == null) {//Activity不存在
                    ...
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (activity.getParent() == null) {//Activity依附的Application不存在
                    ...
                    return WindowManagerGlobal.ADD_APP_EXITING;
                } else if (type == TYPE_APPLICATION_STARTING) {//startingWindow
                    ...
                }
            ...
            } else if (token.asActivityRecord() != null) {
                ...
            }
            ...
            //创建WindowState对象,一个Window对应一个WindowState。this表示WMS本身;session表示WMS提供给窗口使用的IWindowSession;client表示IWindow
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
            //获取DisplayContent对应的窗口策略类DisplayPolicy
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            //通过DisplayPolicy调整窗口属性
            displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
            ...
            //添加窗口权限检查,主要是对系统窗口添加的检查,应用窗口谁都可以添加
            res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
            ...
            //把新添加的窗口对应的WindowState加入到Session的mAddedWindows数组中
            win.mSession.onWindowAdded(win);
            //把客户端对应的binder和对应的WindowState作为键值对加入到mWindowMap数组中
            mWindowMap.put(client.asBinder(), win);
            ...
            //窗口挂载。把WindowState加入到WindowToken中,其实是加入到WindowToken的父类WindowContainer的mChildren中
            win.mToken.addWindow(win);
            displayPolicy.addWindowLw(win, attrs);
            ...
        }
        ...
    }

三:总结

3.1 应用程序通过Session请求WMS添加窗口,并创建匿名binder W,供WMS和自己通信

3.2 addWindow其实主要就做了两件事:获取客户端对应的WindowToken;创建WindowState

并挂载到窗口视图树中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值