🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
首先需要搞清楚WindowManager是什么。 准确的说,WindowManager是一个继承自ViewManager的接口。ViewManager定义了三个函数,分别用于添加/删除一个控件,以及更新控件的布局。 ViewManager接口的另一个实现者是ViewGroup,它是容器类控件的基类,用于将一组控件容纳到自身的区域中,这一组控件被称为子控件。ViewGroup可以根据子控件的布局参数(LayoutParams)在其自身的区域中对子控件进行布局。 读者可以将WindowManager与ViewGroup进行一下类比:设想WindowManager是一个ViewGroup,其区域为整个屏幕,而其中的各个窗口就是一个一个的View。WindowManager通过WMS的帮助将这些View按照其布局参数(LayoutParams)将其显示到屏幕的特定位置。二者的核心工作是一样的,因此WindowManager与ViewGroup都继承自ViewManager。 接下来看一下WindowManager接口的实现者。本章最开始的例子通过Context.getSystemService(Context.WINDOW\_SERVICE)的方式获取了一个WindowManager的实例,其实现如下: **ContextImpl.java-->ContextImpl.getSystemService()** ``` public Object getSystemService(String name) { // 获取WINDOW_SERVICE所对应的ServiceFetcher ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); // 调用fetcher.getService()获取一个实例 returnfetcher == null ? null : fetcher.getService(this); } ``` Context的实现者ContextImpl在其静态构造函数中初始化了一系列的ServiceFetcher来响应getSystemService()的调用并创建对应的服务实例。看一下WINDOW\_SERVICE所对应的ServiceFetcher的实现: **ContextImpl.java-->ContextImpl.static()** ``` registerService(WINDOW_SERVICE, newServiceFetcher() { public Object getService(ContextImpl ctx) { // ① 获取Context中所保存的Display对象 Display display = ctx.mDisplay; /* ② 倘若Context中没有保存任何Display对象,则通过DisplayManager获取系统 **主屏幕所对应的Display对象** */ if (display == null) { DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService( Context.DISPLAY_SERVICE); display = dm.getDisplay(Display.DEFAULT_DISPLAY); } // ③ 使用Display对象作为构造函数创建一个WindowManagerImpl对象并返回 return new WindowManagerImpl(display); }}); ``` 由此可见,通过Context.getSystemService()的方式获取的WindowManager其实是WindowManagerImpl类的一个实例。这个实例的构造依赖于一个Display对象。第4章介绍过DisplayContent的概念,它在WMS中表示一块的屏幕。而这里的Display对象与DisplayContent的意义是一样的,也用来表示一块屏幕。 再看一下WindowManagerImpl的构造函数: **WindowManagerImpl.java-->WindowManagerImpl.WindowManagerImpl()** ``` publicWindowManagerImpl(Display display) { this(display, null); } privateWindowManagerImpl(Display display, Window parentWindow) { mDisplay = display; mParentWindow = parentWindow; } ``` 其构造函数实在是出奇的简单,仅仅初始化了mDisplay与mParentWindow两个成员变量而已。从这两个成员变量的名字与类型来推断,它们将决定通过这个WindowManagerImpl实例所添加的窗口的归属。 说明 WindowManagerImpl的构造函数引入了一个Window类型的参数parentWindow。Window类是什么呢?以Activity为例,一个Activity显示在屏幕上时包含了标题栏、菜单按钮等控件,但是在setContentView()时并没有在layout中放置它们。这是因为Window类预先为我们准备好了这一切,它们被称之为窗口装饰。除了产生窗口装饰之外,Window类还保存了窗口相关的一些重要信息。例如窗口ID(IWindow.asBinder()的返回值)以及窗口所属Activity的ID(即AppToken)。在6.6.1 介将会对这个类做详细的介绍。 也许在WindowManagerImpl的addView()函数的实现中可以找到更多的信息。 **WindowManagerImpl.java-->WindowManagerImpl.addView()** ``` publicvoid addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); } ``` WindowManagerImpl.addView()将实际的操作委托给一个名为mGlobal的成员来完成,它随着WindowManagerImpl的创建而被初始化: ``` privatefinal WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); ``` 可见mGlobal的类型是WindowManagerGlobal,而且WindowManagerGlobal是一个单例模式——即一个进程中最多仅有一个WindowManagerGlobal实例。所有WindowManagerImpl都是这个进程唯一的WindowManagerGlobal实例的代理。 此时便对WindowManager的结构体系有了一个清晰的认识,如图6-2所示。 :-: ![](http://img.blog.csdn.net/20150814133444496?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图 6 - 2 WindowManager的结构体系 - ViewManager接口:WindowManager体系中最基本的接口。WindowManager继承自这个接口说明了WindowManager与ViewGroup本质上的一致性。 - WindowManager接口:WindowManager接口继承自ViewManager接口的同时,根据窗口的一些特殊性增加了两个新的接口。getDefaultDisplay()用以得知这个WindowManager的实例会将窗口添加到哪个屏幕上去。而removeViewImmediate()则要求WindowManager必须在这个调用返回之前完成所有的销毁工作。 - WindowManagerImpl类:WindowManager接口的实现者。它自身没有什么实际的逻辑,WindowManager所定义的接口都是交由WindowManagerGlobal完成的。但是它保存了两个重要的只读成员,它们分别指明了通过这个WindowManagerImpl实例所管理的窗口将被显示在哪个屏幕上,以及将会作为哪个窗口的子窗口。因此在一个进程中,WindowManagerImpl的实例可能有多个。 - WindowManagerGlobal类:它没有继承上述任何一个接口,但它是WindowManager的最终实现者。它维护了当前进程中所有已经添加到系统中的窗口的信息。另外,在一个进程中仅有一个WindowManagerGlobal的实例。 在理清了WindowManager的结构体系后,便可以探讨WindowManager是如何完成窗口管理的。其管理方式体现在其对ViewManager的三个接口的实现上。为了简洁起见,我们将直接分析WindowManagerGlobal中的实现。