Window学习记录

145 阅读5分钟

本文仅作为笔者自己的记录,后有贴原文链接4

Window

window是什么

window机制就是为了管理屏幕上的view的显示以及触摸事件的传递问题。 view数是window机制的操作单位,每一个view树对应一个window,view树是window的存在形式,window是view的载体。我们看到的都不是window而是view树,window是view树的载体,是view树的管理者。 window在源码中的存在形式,大概就是windowManagerService中每个view对应一个windowStatus。 window属性的常量值大部分存储在WindowManager.LayoutParams类中,我们可以通过这个类来获得这些常量,当然还有Gravity类和PixelFormat类等

window的添加过程,指的是通过WindowManagerImpl的addView方法来添加window的过程。想要添加一个window,我们知道首先得有view和WindowManager.LayoutParams对象,才能去创建window。

Button button = new Button(this);
WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams();
// 这里对windowParam进行初始化
windowParam.addFlags...
// 获得应用PhoneWindow的WindowManager对象进行添加window
getWindowManager.addView(button,windowParams);

接下来进入addView方法中看看。windowManager的实现类是WindowManagerImpl。

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

可以看到addView()方法中把逻辑交给了mGlobal进行处理,mGlobal是WindowMangerGlobal,是一个全局单例,是WindowManager接口的具体逻辑实现。

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    // 首先判断参数是否合法
    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;
    // 如果不是子窗口,会对其做参数的调整
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        final Context context = view.getContext();
        if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
    }
    
	synchronized (mLock) {
        ...
        // 这里新建了一个viewRootImpl,并设置参数
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);

        // 添加到windowManagerGlobal的三个重要list中,后面会讲到
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // 最后通过viewRootImpl来添加window
        try {
            root.setView(view, wparams, panelParentView);
        } 
        ...
    }  
}

总给做的工作,分为以下几步:

  • 首先对参数进行合法性检查
  • 检查是否是子view,如果是的话需要对窗口进行调整
  • 新建ViewRootImpl对象,并把view,viewRootImpl、params三个对象添加到list中进行保存
  • 最后通过viewRootImpl来进行添加

在WindowManagerGlobal中有三个List,分别是:

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>();

每个window对应的三个对象都会保存在这里,之后对windiw的一些操作就可以直接来这里取对象了。当window被删除的时候,这些对象也会被从list中移除。

window的添加的逻辑交给了ViewRootImpl,viewRootImpl是window和view之间的桥梁,viewRootImpl可以处理两边的对象,然后联结起来。

ViewRoot怎么处理:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        ...
        try {
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            // 这里调用了windowSession的方法,调用wms的方法,把添加window的逻辑交给wms
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                    mTempInsets);
            setFrame(mTmpFrame);
        } 
        ...
    }
}

viewRootImpl的逻辑很多,重要的是调用了mWindowSession的方法调用了WMS的方法。

mWindowSession是一个IWindowSession对象,看到这个命名很快可以想到这里用了AIDL跨进程通信。IWindowSession是一个IBinder接口,他的具体实现类在WindowManagerService,本地的mWindowSession只是一个Binder对象,通过这个mWindowSession可以调用WMS的方法进行跨进程通信。 mWindowSession来自于ViewRootImpl的构造方法:

public ViewRootImpl(Context context, Display display) {
...
mWindowSession = WindowManagerGlobal.getWindowSession();
...

}

可以看到session来自windowManagerGloval,再深入看一下
```java
public static IWindowSession getWindowSession() {

synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { ... sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { ... }); } ... } return sWindowSession; } }

session是一个单例,也就是说整个一个用的所有viewRootImpl的windowSession都是同一个,也就是一个应用只有一个windowSession。对于wms而言,他是服务于多个应用的,如果说每个viewRootImpl整个session,那么WMS的任务就过于繁重。WMS的对象单位应该是应用,他在内部给每个应用Session分配了一些数据结构,如list,用于保存每个应用的window以及对应的viewRootImpl。当需要操作view的时候,通过session直接找到viewRootImpl就可以操作了。

后面的逻辑就是WMS处理,WMS会创建window,然后结合参数计算window的高度等等,最后使用viewRootImpl进行绘制。 WindowManager是继承ViewManager接口的,viewManager还有另外两个接口:removeView、updateView。

总结

window的添加过程是通过PhoneWindow对应WindowManagerImpl来添加window,内部会调用windowManagerGlobal来实现。WindowManagerGlobal会使用ViewRootImpl来进行跨进程通信,让WMS执行创建Window的业务。每个应用都有一个WindowSession,用于负责和WMS的通信,如ApplicationThread与AMS的通信。

image.png

相关的类

Window的实现只有一个:PhoneWindow,他继承自Window抽象类

WindowManager

就是window的管理类。关键类:windowManager,ViewManager,WindowManagerImpl,WindowManagerGlobal。 windowManager是一个接口,继承自viewManager。ViewManager中包含三个熟悉的接口,addView(),removeView(),updateView()。 WindowManagerImpl和PhoneWindow是成对出现,前者负责管理后者。WindowManagerImpl是WindowManager的实现类,但是他并没有真正实现WindowManager的逻辑,而是交给了WindowManagerGlobal,在windowManagerImpl的内部使用了桥接模式,windowManagerGlobal是全局单例,也是WindowManager的逻辑的真正实现。

view相关

有一个重要的ViewRootImpl,每个view树都会有一个。当我使用WindowManager的addView时,就会创建一个ViewRootImpl。ViewRootImpl的作用很关键:

  • 负责连接view和window的桥梁事务
  • 负责与windowManagerService的事务
  • 负责管理和绘制view树
  • 事件的中转站

每个window都会有一个viewRootImpl,ViewRootImpl是负责绘制这个view树和window与view的桥梁。

WindowManagerService

是window的真正的管理者,类似于AMS。所有window创建最终都要经过WMS。整个Android中,WMS是处于绝对的核心地位,他决定了屏幕所有的window该如何显示如何分发点击事件等等。

[](Android全面解析之Window机制 - 掘金)