深入Android系统(十三)Android的窗口系统

Android的窗口系统由WindowManagerService管理,包括增加和删除窗口,确定窗口的大小和位置,以及实现窗口切换、窗口动画等功能。WindowManagerService名字较长,后面简称WMS

前言

一个小问题

在 Android 中Activity可以理解成 Android 中的一个页面,前面的章节我们说 SurfaceFlinger 最后会把 Surface 对应的 Layer 合成到屏幕上。那么Activity 和 Surface 的关系又是怎么关联的呢?

让我们一起排查下~~~

应用进程和WMS的联系

在学习WMS之前,我们先了解下应用和WMS之间的联系。对于应用来说:

  • Activity

    • Activity并不负责视图控制,它只是控制生命周期和处理事件
    • 真正控制视图的是Window。一个Activity包含了一个WindowWindow才是真正代表一个窗口
    • Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View进行交互
  • Window

    • Window是视图的承载器,内部持有一个DecorView,而这个DecorView才是view的根布局
    • Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow
    • PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中``传递的布局
    • Window通过WindowManagerDecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互
  • ViewRoot

    • 所有View的绘制以及事件分发等交互都是通过它来执行或传递的
    • ViewRoot对应的实现类是ViewRootImpl类,它是连接WindowManagerServiceDecorView的纽带
    • View的三大流程:测量(measure)、布局(layout)、绘制 (draw))均通过ViewRoot来完成

除了上面这三部分,还有一个是在Android应用中存在的一个唯一的WindowManagerGlobal对象,它们的类图关系如下:

应用中的Window对象

应用中的每个Activity对象都有一个类型为Window的成员变量mWindow,定义如下:

java 复制代码
class Activity {
    ...
    private Window mWindow;
    ...
}

Window对象在应用进程代表了一个窗口,这是一块矩形的图像绘制区域。

WMS的角度来说,它并不关系应用中的各种View,他管理和调度的对象是WindowActivity中的View树不管多复杂,最后输出的也仅仅是一张各个View合成的图像而已。

Window是一个抽象类,在Activity类的attach()方法中会对成员变量mWindow进行初始化,代码如下:

java 复制代码
    final void attach(...) {
        ...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
    }

attach()方法中mWindow实例化成了一个PhoneWindow对象。

而对于PhoneWindow对象,需要我们关注的部分是如下两个成员变量:

java 复制代码
public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    ViewGroup mContentParent;
    ...
}
  • mDecor的类型是DecorView,它是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。

    • DecorView作为顶级View,它加载的资源文件内部会包含一个android:id="@android:id/content"ViewGroup
    • 这个ViewGroup便是后面用来添加setContentView()方法传入的layout布局文件
    • DecorView具体加载哪种布局文件和主题有关,这部分在PhoneWindowgenerateLayout()方法中体现
  • mContentParent的类型是ViewGroup,它其实就是android:id="@android:id/content"对应的组件

    • 跟踪generateLayout()方法就会发现如下代码:
    java 复制代码
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    • contentParent创建完成后便会赋值给mContentParent,而此处的ID_ANDROID_CONTENT的定义是在Window.java
    java 复制代码
        public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

应用中的WindowManager对象

应用中的每个Activity对象都有一个类型为WindowManager的成员变量mWindowManager,定义如下:

java 复制代码
class Activity {
    ...
    private WindowManager mWindowManager;
    ...
}

mWindowManager用于和WMS通信,不过WindowManager是一个接口类,定义如下:

java 复制代码
public interface WindowManager extends ViewManager {...}

mWindowManager也是在attach()方法中进行的初始化,相关代码如下:

java 复制代码
    final void attach(...) {
        ...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...
        mWindowManager = mWindow.getWindowManager();
        ...
    }

attach()方法中,对mWindowManager变量的赋值是调用mWindowgetWindowManager()方法完成的。

但是在这之前,attach()方法中还调用了mWindow对象的setWindowManager()方法。前面我们已经知道mWindow对象的类型是PhoneWindow,不过这个setWindowManager()方法是在基类Window中实现的,代码如下:

java 复制代码
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

setWindowManager()方法中,调用了WindowManagerImpl类的createLocalWindowManager()方法创建了mWindowManager对象,这个方法也很简单:

java 复制代码
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

createLocalWindowManager()方法中新创建了一个WindowManagerImpl对象,因此,每个Activity中的mWindowManager对象实际上是一个WindowManagerImpl对象,我们再看下这个类的定义:

java 复制代码
public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    ...
}

WindowManagerImpl中定义了一个mGlobal变量,它的值是通过WindowManagerGlobal.getInstance()得到的,看样子是个单例模式,代码如下:

java 复制代码
    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

getInstance()方法中创建了一个全局唯一的WindowManagerGlobal对象并保存到静态变量sDefaultWindowManager

WindowManagerImpl中各种方法的实现只是在转调WindowManagerGlobal类中的方法,由此可见,一个应用中所有Activity都是通过这个进程内唯一的WindowManagerGlobal对象和WMS通信

WindowManagerGlobal类中还有3个重要的成员变量:

java 复制代码
public final class WindowManagerGlobal {
    ...
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    ...
}
  • mViews:保存了应用中所有顶层View对象,也就是DecorView(稍后细讲)
  • mRoots:保存的是和顶层View对象关联的ViewRootImpl对象
  • mParams:保存的是创建顶层Viewlayout参数

建立应用和WMS的联系

我们知道在ActivityThread中的handleResumeActivity()中会创建ViewRootImpl对象并进行显示,我们来看下ViewRootImpl中关键的几个变量:

java 复制代码
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    ...
    final IWindowSession mWindowSession;
    ...
    final W mWindow;
    ...
}

变量的初始化是在构造方法中,代码如下:

java 复制代码
    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ...
        mWindow = new W(this);
        ...
    }

重点是进行了mWindowSessionmWindow的创建,我们逐一看下

IWindowSession对象的创建

mWindowSession变量的类型是IWindowSession,它是WMS中某个Binder对象的引用对象,mWindowSession的值是通过WindowManagerGlobalgetWindowSession()方法获得的,代码如下:

java 复制代码
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (
            `sWindowSession`== null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

如果sWindowSession为空,先获取InputMethodManager对象,然后调用WMSopenSession()接口来进行创建,代码如下:

java 复制代码
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }

openSession()直接创建了一个Session对象,这个Session类我们图像显示部分简单介绍过,它是一个Binder服务类,简要定义如下:

java 复制代码
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {...}

对于用户进程而言,获取到的便是这个Session对象的Binder引用对象,并保存在了WindowManagerGlobal的静态变量sWindowSession中。

因为WindowManagerGlobal在进程中属于单例的存在,因此,所有调用getWindowSession()方法返回的都是这个sWindowSession对象

因此,一个用户进程中的所有ViewRootImpl对象中的mWindowSession都引用的是相同的对象 。而这个mWindowSession对象的作用便是向WMS发起Window相关的Binder调用

应用进程通过sWindowSession对象可以调用到WMS的功能方法了,那么WMS是如何通知或者控制应用进程的呢?

W

图像显示部分已经介绍过ActivityThreadhandleResumeActivity()方法了,最后会调用到WindowManagerGlobal对象的addView()方法,方法内容如下:

java 复制代码
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            ...
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            try {
                root.setView(view, wparams, panelParentView);
            }
            ...
        }
    }

addView()方法中最重要的是创建了ViewRootImpl对象,并通过setView()把它和顶层的View对象关联在了一起。setView()方法简要如下:

java 复制代码
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            ...
            requestLayout();
            ...
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                res = mWindowSession.addToDisplay(mWindow, ...);
            }
            ...
        }
    }
}

setView()方法比较长,我们需要关注的是:

  • 方法中对ViewRootImpl对象和顶层View之间的关联只会执行一次,如果mView不为空,不会再次赋值
  • 方法中调用了SessionaddToDisplay()方法,更为重要的是参数mWindow

mWindow的类型是W,它是一个ViewRootImpl的静态内部类,定义如下:

java 复制代码
    static class W extends IWindow.Stub {
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;
        ...
    }

可以看出W是一个实现了IWindow接口的Binder服务类,并且在ViewRootImpl中通过Session.addToDisplay()方法将WBinder引用对象传递给了WMS

我们看下源码中对IWindow接口的描述:

API back to a client window that the Window Manager uses to inform it of interesting things happening.

可以看出WMS通过IWindow接口来通知应用,接下来看下WMS中是如何处理这个对象的

秋地麻袋~,还记得我们前言中的问题吗,快看setView中的requestLayout方法

是不是勾起了作为应用开发者的回忆

Surface 和 Activity 的联系

ViewRootImpl.setView() 会触发 requestLayout(),最终执行到核心方法 performTraversals()(这是 View 绘制三大流程 Measure/Layout/Draw 的入口)

performTraversals() 800 多行。。。简要代码如下:

java 复制代码
// 伪代码逻辑 ViewRootImpl.java
private void performTraversals() {
    // ...
    // 1. 通过 IPC 请求 WindowManagerService (WMS) 对窗口进行布局
    // mSurface 是 ViewRootImpl 的成员变量
    relayoutResult = mWindowSession.relayout(..., mSurface, ...);

    // 2. WMS 在底层创建 SurfaceControl,并将 Native 层的 Surface 信息
    // 回写(Copy)到 ViewRootImpl 的 mSurface 中。
    // 后面会细讲
    
    if (mSurface.isValid()) {
        // Surface 此时已经准备好,可以合成了
    }
    // ...
    performMeasure(...);
    performLayout(...);
    performDraw(...);
}

relayout 过程中,WMS 会与 SurfaceFlinger 通信,分配 Layer(图层)。WMS 将生成的 Surface 信息填充到 ViewRootImpl 传入的 mSurface 参数中。

这样,一个 Activity 就和 SurfaceFlinger 中的 Surface 产生了关联啦。

WMS中建立和应用的关系

前面提到ViewRootImpl对象会通过addToDisplay()方法将IWindowBinder引用对象传递到WMS中,我们先看下addToDisplay()方法:

java 复制代码
    // 方法定义在 com.android.server.wm.Session 中
    @Override
    public int addToDisplay(IWindow window, ...) {
        // mService 的类型便是 WindowManagerService
        return mService.addWindow(this, window, ...);
    }

调用了WMSaddWindow()方法,代码如下:

java 复制代码
public int addWindow(Session session, IWindow client, ...) {
    ...
    synchronized(mWindowMap) {
        ...
        if (mWindowMap.containsKey(client.asBinder())) {
            Slog.w(TAG_WM, "Window " + client + " is already added");
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }
        ...
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
        ...
        win.attach();
        mWindowMap.put(client.asBinder(), win);
        win.initAppOpsState();
        ...
    }
    ...
    return res;
}

addWindow()方法代码在200多行,此处我们需要关注的部分是:

  • 创建了WindowState对象并添加到mWindowMap集合中
  • 调用WindowState对象的attach()方法

WindowState后面会详细介绍,此处的关键定义如下:

java 复制代码
/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
    ...
    final Context mContext;
    final Session mSession;
    final IWindow mClient;
    ...
    void attach() {
        if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
        mSession.windowAddedLocked(mAttrs.packageName);
    }
}
  • mClient是应用进程中Binder服务对象W的引用对象
  • mSession便是WMS对外封装的对象之一。以前面的addToDisplay()为例,实际上只是转调了WMSaddWindow()方法
  • attach()方法也只是调用了mSession对象的windowAddedLocked()方法

继续看下windowAddedLocked()方法:

java 复制代码
    void windowAddedLocked(String packageName) {
        mPackageName = packageName;
        mRelayoutTag = "relayoutWindow: " + mPackageName;
        if (mSurfaceSession == null) {
            mSurfaceSession = new SurfaceSession();
            mService.mSessions.add(this);
            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                mService.dispatchNewAnimatorScaleLocked(this);
            }
        }
        mNumWindow++;
    }

windowAddedLocked()方法

  • 首先检查了mSurfaceSession的值,当为空时才会创建SurfaceSession对象
    • 前面图像显示部分介绍过,SurfaceSession对象是用来创建Surface
  • 然后将Session对象本身添加到WMSmSessions列表中

到这里,应用进程就和WMS各自持有了对方的Binder引用对象,形成了一个双向Binder通道,示意图如下:

DecorView

关于DecorView,还是要从我们最熟悉的地方说起。

ActivityonCreate()中调用setContentView()方法时,我们看下发生了什么:

java 复制代码
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

ActivityonCreate()调用的是Window类的setContentView(),这是个抽象方法,具体的实现在PhoneWindow.java中,代码如下:

java 复制代码
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        }
        ...
    }

如果mContentParent对象还没有创建出来,则会调用installDecor()方法,方法如下:

java 复制代码
    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1); // 创建 DecorView
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor); // 为DecorView设置布局格式,并返回 mContentParent
        }
    }

installDecor()方法

  • 先根据成员变量mDecor是否为空来决定是否进行DecorView的创建

    • 对象的创建方法为generateDecor(),内容如下:
    java 复制代码
    protected DecorView generateDecor(int featureId) {
        Context context;
        ... // 省略 context 的创建过程
        return new DecorView(context, featureId, this, getAttributes());
    }
  • 然后通过generateLayout()方法为创建好的DecorView设置布局格式,并返回mContentParent对象

我们再看下DecorView的定义

java 复制代码
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    ...
}

其实就是个自定义的ViewGroup,只是这个DecorViewAndroid中作为顶级View去使用

WindowManagerService服务

WMS服务的启动,要从SystemServer中的startOtherServices()方法说起,关键代码如下:

java 复制代码
private void startOtherServices() {
    ...
    wm = WindowManagerService.main(..., new PhoneWindowManager());
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
            DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ...
}

看下main()方法的定义:

java 复制代码
    public static WindowManagerService main(..., WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

main()方法主要是初始化了WindowManagerService对象,我们需要注意的是它的最后的参数WindowManagerPolicy policy,它的具体的实现类是PhoneWindowManager

再看下WindowManagerService的关键定义:

java 复制代码
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    ...
    final ArraySet<Session> mSessions = new ArraySet<>();
    final WindowHashMap mWindowMap = new WindowHashMap();
    final WindowManagerPolicy mPolicy;
    ...
    private WindowManagerService(..., WindowManagerPolicy policy) {
        ...
        mPolicy = policy;
        ...
    }
}

其中的关键成员如下:

  • mSessions中储存的是Session服务类的对象,每个应用在WMS中都有一个对应的Session对象,它们都保存在mSessions

  • mWindowMap中类型是WindowHashMap,保存的是所有窗口的WindowState对象。WindowHashMap定义如下:

    java 复制代码
    class WindowHashMap extends HashMap<IBinder, WindowState> {
    }
    • 此处并未做任何删减哈,源码中就是简单地继承了HashMap<IBinder, WindowState>
    • WindowState表示一个窗口的所有属性,它便是是WMS中的窗口。等下细讲
  • mPolicyWMS中执行窗口管理策略的对象,类型是WindowManagerPolicy,这是一个接口类

    • SystemServerstartOtherServices()中可以看出具体的实现类是PhoneWindowManager
    • AndroidWMS只是负责窗口管理的框架部分,通过策略模式将框架和窗口实现细节进行了分离

对比9.05.0源码,WMS关于窗口部分设计变化很大,在了解Android如何添加一个窗口前,我们先看下Android中窗口类型的定义

窗口类型

WMS中定义了3中窗口:应用窗口子窗口系统窗口,相关的定义都是在frameworks/base/core/java/android/view/WindowManager.java中,确切的说应该是在WindowManager.LayoutParams类中定义

应用窗口(application window)

应用窗口是Activity使用的全屏窗口,Android定义了4种不同类型的应用窗口

  • TYPE_BASE_APPLICATION:基础应用窗口,应用中所有窗口都位于它的上层。只能由系统创建
  • TYPE_APPLICATIONActivity的顶层窗口,应用中最常见的一种
  • TYPE_APPLICATION_STARTING:应用启动时由系统创建的窗口,显示一些信息,直到应用创建的窗口显示出来
  • TYPE_DRAWN_APPLICATION:类似于普通的TYPE_APPLICATIONWMS针对类型额外增加了等待机制

子窗口(sub-window)

子窗口是指依附于应用窗口或系统窗口的窗口类型,它们一般随所依附的窗口一起出现或切换到后台。Android中定义了6种子窗口类型

  • TYPE_APPLICATION_PANEL:应用Panel窗口,显示在依附窗口的上层
  • TYPE_APPLICATION_MEDIA:应用Media窗口,通常包含独立的Surface,显示在依附窗口的下层
  • TYPE_APPLICATION_SUB_PANEL:应用子Panel窗口,显示在依附窗口和TYPE_APPLICATION_PANEL的上层
  • TYPE_APPLICATION_ATTACHED_DIALOG:和TYPE_APPLICATION_PANEL类似,但是窗口的布局方式和Activity的顶层窗口相同
  • TYPE_APPLICATION_MEDIA_OVERLAY:一种显示在TYPE_APPLICATION_MEDIA和所依附窗口之间的半透明窗口。一般用来显示Video的字幕或者相机的操作提示界面
  • TYPE_APPLICATION_ABOVE_SUB_PANEL:一种显示在TYPE_APPLICATION_SUB_PANEL之上的窗口

系统窗口(system window)

系统窗口大都用于系统生成的UI中,一般比较独立,种类非常多,我们挑几个常见的看下

  • TYPE_STATUS_BAR:状态栏窗口,系统中只能有一个,在屏幕的最上方
  • TYPE_SEARCH_BAR:搜索栏窗口,系统中只能有一个,在屏幕的最上方
  • TYPE_PHONE:电话窗口,提供用户与电话的交互界面,通常位于所用应用窗口的上方,但是在状态栏的后面
  • TYPE_SYSTEM_ALERT:系统警告窗口,位于所用应用窗口的上方
  • TYPE_TOAST:临时通知窗口,显示一些提示信息

还有很多就不一一列举了,Android系统窗口的定义有39种之多,源码中有着详细的注释,大家可以在WindowManager.java中找到(传送门)

那么WMS中是怎么定义窗口的呢?

WMS中窗口的设计

前面讲过(WMSaddWindow()方法),当向WMS添加一个窗口时,WMS会为其创建一个WindowState。对于WindowState来说,它包含了一个窗口的所有属性,所以它是WMS中真正意义上的窗口

先看下WindowState的关键定义:

java 复制代码
/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
    ...
    WindowToken mToken;
    AppWindowToken mAppToken;
    ...
    final int mBaseLayer;
    final int mSubLayer;
}

WindowState继承了WindowContainer,又是和书中不一样的地方

对比5.0源码,很明显在Window这部分Android做了重构(设计模式看上去像是组合模式),优化了类结构,抽象出了WindowContainer用来支撑起整个窗口体系

基于Android 9.0源码,窗口体系的关系梳理如下:

类图中涉及到的类的信息下:

  • WindowContainer

    • 成员变量mChildren的类型是WindowList,看实现其实就是ArrayList<WindowState>。又因为WindowToken继承了WindowContainer,所以该成员变量子类都会继承过来
    • 所有拥有相同Token的窗口(WindowState)都会存放在mChildren中,
  • WindowToken

    • 成员变量tokenIBinder对象,具有系统唯一性,向WMSmWindowMap中添加对象时都是使用这个token
    • 定义了AppWindowToken asAppWindowToken(),默认返回null
  • AppWindowToken

    • 继承自WindowToken并重写了AppWindowToken asAppWindowToken()方法,返回this
    • 这样便可以通过asAppWindowToken()的返回值判断是那种Token
  • DisplayContent

    • 继承自WindowContainer,真正意义上的屏幕,WMS中一个DisplayContent实例表示一个屏幕。多屏幕会对应多个实例
    • 内部定义了4种存放Window的容器,都是直接或间接继承自WindowContainer
      • mTaskStackContainers:用来保存所有的应用窗口
      • mAboveAppWindowsContainers:用来保存显示在应用窗口的上面的非应用窗口
      • mBelowAppWindowsContainers:用来保存显示在应用窗口的下面的非应用窗口
      • mImeWindowsContainers:用来保存 IME Window
  • RootWindowContainer

    • 代表WMS中窗口的根容器,所有DisplayContent的实例都会添加到其中
    • RootWindowContainerWMS的构造方法中初始化为mRoot对象

还是回到WindowState,类中定义了两个常量mBaseLayermSubLayer,通过这两个常量便可以确定一个Window所在的层级。这部分就不细讲了哈,涉及点有点多,后面用到再来补充吧,嘻嘻

我们重点看下面两个知识点:WMSaddWindowrelayoutWindow的设计

addWindow方法

addWindow()WMS中建立和应用的关系已经介绍,不再赘述

relayoutWindow方法

前面介绍过,在ActivityonResume阶段,通过ViewRootImplsetView方法开始渲染View的流程。

setView方法中会通过requestLayout()开始测量渲染,这个方法中有一个核心的逻辑就是调用WMSrelayoutWindow(),重新测量Window,简要流程如下:

java 复制代码
//ViewRootImpl.java
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        ...
        scheduleTraversals();
    }
}
private void performTraversals() {
    ...
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    ...
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
    ...
    int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
            insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
            mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
            mPendingMergedConfiguration, mSurface);
    ...
    return relayoutResult;
}

relayoutWindow()通过Sessionrelayout()方法调用到WMSrelayoutWindow()方法

java 复制代码
    @Override
    public int relayout(IWindow window, int seq, ...) {
        ...
        int res = mService.relayoutWindow(this, window, seq, attrs,
                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                outStableInsets, outsets, outBackdropFrame, cutout,
                mergedConfiguration, outSurface);
        ...
        return res;
    }

WMS中的relayoutWindow()代码比较复杂,我们简单看下:

java 复制代码
    public int relayoutWindow(...) {
        ...
        synchronized(mWindowMap) {
            // 从 mWindowMap 取出对应的 WindowState 对象
            WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            displayId = win.getDisplayId();
            ...
            // 调用 WindowSurfacePlacer 的 performSurfacePlacement 方法
            // 这个方法其实就是检查清理无用的 Surface 对象,为后面创建 Surface 做准备
            // 详细的方法就不贴出来了,大家可以自行阅读
            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
            if (shouldRelayout) {
                result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
                try {
                    // 创建 Surface ,确切的说应该是创建 WindowSurfaceController 对象
                    // WindowSurfaceController 其实就是对 Surface 和 SurfaceControl 的封装
                    // Surface 和 SurfaceControl 的关系在十二章已经介绍啦
                    result = createSurfaceControl(outSurface, result, win, winAnimator);
                } 
                ...
            } else {
                ...
            }
            if (focusMayChange) {
                // 如果窗口发生了变化,调用 updateFocusedWindowLocked 方法重新测量窗口大小
                // 这个方法才是真正的核心测量窗口大小逻辑
                // 方法中会调用 DisplayContent.performLayout 方法开始真正的测量
                if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                        false /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }
            ...
            win.mInRelayout = false;
        }
        ...
    }

WMSrelayoutWindow() 方法中,核心逻辑在于 createSurfaceControl 的调用。

java 复制代码
result = createSurfaceControl(outSurface, result, win, winAnimator);

这里的参数 outSurface 至关重要。回看 ViewRootImpl 调用 relayout 的代码:

java 复制代码
// ViewRootImpl.java
mWindowSession.relayout(..., mSurface);

我们可以明确看到,ViewRootImpl 将自己的成员变量 mSurface 传递给了 WMS。 在 WMS 内部,createSurfaceControl 会请求 SurfaceFlinger 创建图层(Layer),并将生成的 NativeSurface 信息回写(Copy)到传入的 outSurface 参数中。

这意味着: 当 relayoutWindow 调用返回时,应用进程 ViewRootImpl 中的 mSurface 对象就已经持有了真正的图形缓冲区引用,随后应用便可以利用这个 Surface 进行绘图。

怎么说~怎么说~。前言的问题是不是明了啦

总结

Android 的窗口系统是一个典型的 C/S 架构,由应用进程(Client)和 WindowManagerService(Server)共同构成:

应用端 (Client)

  • Activity 负责生命周期,持有一个 PhoneWindow 对象来管理视图策略。

  • DecorView 是视图树的根节点。

  • ViewRootImpl 是核心管理者,它连接了 DecorView 和 WMS,并持有 Surface 对象,负责发起绘制流程(Measure/Layout/Draw)。

  • WindowManagerGlobal 是进程单例,负责管理当前进程所有的 ViewRootImplView

通信层 (IPC)

  • IWindowSession:应用向 WMS 发送请求的通道(如 addToDisplay, relayout)。

  • IWindow (W类):WMS 回调应用的通道(如窗口焦点变化、配置改变)。

服务端 (WMS)

  • WindowStateWMS 中代表一个窗口的实体,保存窗口的所有属性。

  • WindowContainer:构建了层级化的窗口容器结构(DisplayContent -> TaskStack -> WindowState)。

  • Surface 管理:WMSrelayoutWindow 过程中计算窗口大小与位置,并与 SurfaceFlinger 通信创建 SurfaceControl,最终将可用的 Surface 返回给应用端。

相关推荐
莞凰5 小时前
昇腾CANN的“灵脉根基“:Runtime仓库探秘
android·人工智能·transformer
NiceCloud喜云6 小时前
Claude Files API 深入:从上传、复用到配额管理的工程化指南
android·java·数据库·人工智能·python·json·飞书
ujainu6 小时前
CANN pto-isa:虚拟指令集如何连接编译与执行
android·ascend
赏金术士7 小时前
第六章:UI组件与Material3主题
android·ui·kotlin·compose
TechMerger8 小时前
Android 17 重磅重构!服役 20 年的 MessageQueue 迎来无锁改造,卡顿大幅优化!
android·性能优化
yuhuofei202111 小时前
【Python入门】Python中字符串相关拓展
android·java·python
dalancon11 小时前
Android Input Spy Window
android
刀法如飞12 小时前
Palantir Ontology 存储结构与读写机制原理深入剖析
大数据·设计模式·系统架构
dalancon13 小时前
InputDispatcher派发事件,查找目标窗口
android
我命由我1234513 小时前
Android Framework P3 - MediaServer 进程、认识 ServiceManager 进程
android·c语言·开发语言·c++·visualstudio·visual studio·android runtime