WindowManagerService

在Android开发中,Window是所有视图的载体,如Activity,Dialog,Toast,PopupWindow等等,我们想要对Window进行添加和删除就要通过WindowManager来操作,而WindowManager通过Binder机制与WindowManagerService进行跨进程通信,最终把具体的实现工作交给WindowManagerService。

相关源码位置如下:

frameworks/base/core/java/android/view/*.java(*代表Window, WindowManager, ViewManager, WindowManagerImpl,WindowManagerGlobal, ViewRootImpl)
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java	
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java	
frameworks/base/services/core/java/com/android/server/wm/Session.java

Android视图层次结构简介

先简单介绍一下Android视图层次结构:
在这里插入图片描述

Activity

控制器,控制生命周期,不控制视图,控制视图的是Window,每个Activity里面都有一个Window对象。

每一个Activity维护着一个ContextImpl对象和一个Window对象。

Window

承载器,承载着View。

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 */
public abstract class Window {
    public static final int FEATURE_NO_TITLE = 1;
    public static final int FEATURE_CONTENT_TRANSITIONS = 12;
    //...
     public abstract View getDecorView();
     public abstract void setContentView(@LayoutRes int layoutResID);
     public abstract void setContentView(View view);
     public abstract void setContentView(View view, ViewGroup.LayoutParams params);
     public <T extends View> T findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }
    //...
}

可以看到里面有我们熟悉的一些字段和方法,以Activity对应的Window为例,具体的实现类是PhoneWindow,在PhoneWindow中有一个顶级View——DecorView,继承自FrameLayout,我们可以通过getDecorView()获得它,当我们调用Activity的setContentView()时,其实最终会调用Window的setContentView(),当我们调用Activity的findViewById()时,其实最终调用的是Window的findViewById(),这也间接的说明了Window是View的直接管理者。但是Window并不是真实存在的,它更多的表示一种抽象的功能集合,View才是Android中的视图呈现形式。Android中需要依赖Window提供视图的有Activity,Dialog,Toast,PopupWindow,StatusBarWindow,输入法窗口等,因此,Activity、Dialog等视图都对应着一个Window。

在实际使用中,无法直接访问Window,对Window的访问必须通过WindowManager。

App可以没有Activity/PhoneWindow/DecorView,例如带悬浮窗口(通过WindowManager.addView()实现 )的Service;

更好的理解Window,可以参考:Activity的Window对象创建过程

PhoneWindow

Window的唯一实现类。

DecorView

根View,是Android视图树的根节点,是FrameLayout的子类。

ViewRootImpl

作用:

  1. 连接器,连接DecorView与WMS,负责与WMS进行直接的通讯
  2. 控制View的绘制流程,measure,layout,draw
  3. 输入事件的中转站,向DecorView分发事件
  4. 管理Surface

使用:
WindowManagerGlobal里用mRoots维护着每个View对应的ViewRootImpl

 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();

每次调用WindowManager的addView()方法添加一个View时,WindowManagerGlobal都会创建一个新的ViewRootImpl对象,并添加到mRoots。

顾名思义,ViewRootImpl实现了一个View树的根。它负责与WMS进行直接的通讯,负责管理Surface,负责触发View的测量、布局、绘制,同时也是输入事件的中转站,输入事件的派发处理都由ViewRootImpl触发。它是WindowManagerGlobal工作的实际实现者,它需要负责与WMS交互通信以调整窗口的位置大小,以及对来自WMS的事件(如窗口尺寸改变等)作出相应的处理。总之,ViewRootImpl是整个控件系统正常运转的动力所在,无疑是本章最关键的一个组件。

WindowManager

WindowManager的主要功能是提供简单的API使得使用者可以方便地将一个控件作为一个窗口添加到系统中,使得开发者无需关注与WMS复杂的通信过程,通过WindowManager即可简单的添加删除窗口。

WindowManager是一个接口,里面常用的方法有:添加View,更新View和删除View,WindowManager继承自ViewManager,这三个方法定义在ViewManager中,如下:

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

可以看到这些方法传入的参数是View,不是Window,说明WindowManager管理的是Window中的View,我们通过WindowManager操作Window就是在操作Window中的View。WindowManager的具体实现类是WindowManagerImpl,我们看一下相应方法的实现,如下:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    //...
    
    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
    
      @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        //...
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        //...
        mGlobal.updateViewLayout(view, params);
    }
    
     @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}

可以看到WindowManagerImpl也没有做什么,它把3个方法的操作都委托给了WindowManagerGlobal(WindowManagerGlobal对象mGlobal是一个单例,即一个进程中最多仅有一个WindowManagerGlobal实例,WindowManagerImpl实例可以有多个。所以这个进程唯一的WindowManagerGlobal实例是所有WindowManagerImpl的代理。),我们还看到了mParentWindow这个字段,它是Window类型,是从构造中被传入,所以WindowManagerImpl会持有Window的引用,这样WindowManagerImpl就可以对Window做操作了。

分析下mGlobal.addView(),我们可以理解为往window中添加View,在WindowManagerGlobal中,如下:

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow){
    //...
    ViewRootImpl root;
    root = new ViewRootImpl(view.getContext(), display);//注释1
    //...
    root.setView(view, wparams, panelParentView);
}

最终会走到ViewRootlmp的setView中, 如下:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  	//...	
    //这里会进行View的绘制流程
    requestLayout();
     //...
    //通过session与WMS建立通信
     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
    //...
}

在ViewRootlmp的setView()中,首先通过requestLayout()发起View绘制流程,然后调用mWindowSession的addToDisplay()通过Binder机制与WMS进行跨进程通信,请求WMS显示窗口上的视图,至此View就会显示到屏幕上。
这个mWindowSession是一个IWindowSession.aidl接口类型,用来实现跨进程通信,在WMS内部会为每一个应用的请求保留一个单独的Session(继承自IWindowSession.Stub),同样实现了IWindowSession接口,应用与WMS之间的通信就通过这个Session。那么这个mWindowSession什么时候被赋值的呢?就在上面的注释1中,我们打开ViewRootlmp的构造函数,如下:

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

可以看到mWindowSession是通过WindowManagerGlobal的单例类的getWindowSession()获得的,我们打开WindowManagerGlobal的getWindowSession(),如下:

 public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    //1、首先获取WMS的本地代理
                    IWindowManager windowManager = getWindowManagerService();
                    //2、通过WMS的本地代理的openSession()方法来获取Session
                    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;
        }
    }

我们首先看1,getWindowManagerService()源码如下:

public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                //获取WMS的本地代理对象
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
              	//...
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

ServiceManager.getService(“window”)就是获得WMS,然后通过IWindowManager.Stub.asInterface()转换成WMS在应用进程的本地代理。最终getWindowManagerService()就是返回WMS在本地应用进程的代理。(这里涉及到Binder知识)

后看2,通过WMS的本地代理的openSession()方法来获取Session,我们可以在WMS中找到这个函数实现(binder机制,调用WMS的本地代理的openSession()方法最终会调用WMS的openSession()方法),如下:

 @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        //...
        //为每个窗口请求创建一个Session并返回
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }

至此建立起与WMS的通信的桥梁。然后WindowManagerImpl就间接的通过Session向WMS发起显示窗口视图的请求,WMS会向应用返回窗口交互的信息。
mGlobal.updateViewLayout()和mClobal.removeView()也是类似的过程。

WindowManagerImpl

WindowManagerImpl是WindowManager接口的实现类。它自身没有什么实际的逻辑,WindowManager所定义的接口都是委托给WindowManagerGlobal来实现。但是它保存了一个重要的只读成员mParentWindow

 private final Window mParentWindow;

表示这个窗口窗口的父窗口。

WindowManagerGlobal

1.为每个DecorView新建一个对应的ViewRootImpl对象,将DecorView交由新建的ViewRootImpl进行托管。
2.维护着WMS在client进程的代理对象sWindowManagerService以及与WMS建立的Session对象sWindowSession,各个ViewRootImpl对象通过这个sWindowSession与WMS跨进程通信。
3.将DecorView、布局参数以及新建的ViewRootImpl以相同的索引值添加到三个对应的数组mViews、mParams以及mRoots中,以供之后的查询之需。DecorView、布局参数以及ViewRootImpl三者共同组成了客户端的一个窗口,或者说,在视图系统中的窗口就是DecorView、布局参数与ViewRootImpl的一个三元组,WindowManagerGlobal通过三元组维护着当前进程中所有已经添加到系统中的窗口的信息。
WindowManagerGlobal管理窗口的原理如下图所示:
在这里插入图片描述

WindowManagerService

WindowManagerService是一个系统服务,由system_server进程启动,继承自IWindowManager.Stub,它的主要功能分为以下两方面:

1、窗口管理

它负责窗口的启动,添加和删除,它还负责窗口的层级显示(z-orderes)和维护窗口的状态。我们继续上面的mGlobal.addView(),上面讲到这个方法是向WMS发起一个显示窗口视图的请求,最终会走到mWindowSession.addToDisplay()方法,我们可以在Session中找到这个函数实现,如下:

@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        //调用WMS的addWindow()方法
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

可以看到addToDisplay()方法中最终返回了WMS中addWindow()所返回的结果,Window的添加请求就交给WMS去处理,addWindow()的实现在WMS中,里面代码很长,这里就不再详细分析了,addWindow()主要做的事情是先进行窗口的权限检查,因为系统窗口需要声明权限,然后根据相关的Display信息以及窗口信息对窗口进行校对,再然后获取对应的WindowToken,再根据不同的窗口类型检查窗口的有效性,如果上面一系列步骤都通过了,就会为该窗口创建一个WindowState对象,以维护窗口的状态和根据适当的时机调整窗口状态,最后就会通过WindowState的attach()方法与SurfaceFlinger通信。因此SurfaceFlinger能使用这些Window信息来合成surfaces,并渲染输出到显示设备。

2、输入事件的中转站

当我们的触摸屏幕时就会产生输入事件,在Android中负责管理事件的输入是InputManagerService,它里面有一个InputManager,在启动InputManagerService的同时会创建InputManager,在创建InputManager同时创建InputReader和InputDispatcher,InputReader会不断的从设备节点中读取输入事件,InputReader将这些原始输入事件加工后就交给InputDispatcher,InputDispatcher通过WMS将输入事件派发给ViewRootImp,ViewRootImp会把这个输入事件传给DecorView,DecorView通过设置的Window.Callback将这个输入事件传给Activity,接下去就涉及我们熟悉的事件分发机制。

我们来再来看在ViewRootImp的setView中调用mWindowSession.addToDisplay方法时传入的参数:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  	//...	
   	mInputChannel = new InputChannel();
    //...
    //通过session与WMS建立通信,同时通过InputChannel接收输入事件回调
     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
    //...
     if (mInputChannel != null) {
         //...
         //处理输入事件回调
         mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
     }

}

注意这个传入的mInputChannel参数,它实现了Parcelable接口,用于接受WMS返回来的输入事件。

整个体系的事件分发顺序为:
在这里插入图片描述

它们之间的类图

在这里插入图片描述

总结

通过上面的介绍,我们知道Window是View的载体,我们想要对Window进行删除,添加,更新View就得通过WindowManager,WindowManager与WMS通过Session进行通信,具体的实现就交给了WMS处理,WMS会为每一个Window创建一个WindowState并管理它们,具体的渲染工作WMS就交给SurfaceFlinger处理。

在《深入理解Android内核设计思想》一书中看到一个比喻非常好,整个界面就像由N个演员参与的话剧:SurfaceFling是摄像机,它只负责客观的捕捉当前的画面,然后真实的呈现给观众;WMS就是导演,它要负责话剧的舞台效果、演员站位;ViewRootImpl就是各个演员的长相和表情,取决于它们各自的条件与努力。可见,WMS与SurfaceFling的一个重要区别就是——后者只做与“显示”相关的事情,而WMS要处理对输入事件的派发。

参考:

Window, WindowManager和WindowManagerService之间的关系
Android自定义View基础:ViewRoot、DecorView & Window的简介
WMS—启动过程
http://liuwangshu.cn/tags/WindowManager/
简述Activity与Window关系

深入理解Android 卷III 6.2.2 通过WindowManagerGlobal添加窗口
WindowManagerService架构剖析之addWindow流程
WindowManagerService架构剖析之窗口分组与分层
Android系统服务 —— WMS与AMS

Android应用程序输入事件分发和处理机制
Android事件分发机制——ViewRootImpl篇(前传)

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/com/android/internal/policy/DecorView.java

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/ViewRootImpl.java

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/IWindow.aidl

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/IWindowManager.aidl
https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/WindowManagerImpl.java

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/ViewManager.java
https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/WindowManager.java

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/view/WindowManagerGlobal.java

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值