Android的GUI
系统是Android
最重要也最复杂的系统之一。它包括以下部分:
- 窗口和图形系统 - Window and View Manager System.
- 显示合成系统 - Surface Flinger
- 用户输入系统 - InputManager System
- 应用框架系统 - Activity Manager System.
它们之间的关系如下图所示
只有对这些系统的功能和工作原理有基本的了解,我们才能够解答一些经常萦绕在脑海里的问题,比如说:
- Activity启动过程是怎样的?Activity的onXXX()在后台都做了什么工作?Activity的show()和hide()是如何控制的?
- Surface是什么时候被谁创建的?
- Android是如何把一个个的控件画到Surface上的?然后显示到手机屏幕上的?
- 应用程序窗口是如何获取焦点的?用户的按键(触摸屏或键盘)是怎样传递到当前的窗口?
- Android是一个多窗口的系统吗?
- Android是怎么支持多屏互动的?(Wifi Display)
- 输入法窗口到底属于哪个进程?为什么不管什么应用,只要有输入框的地方都能弹出它?
本文将从框架和流程角度出发,试图对Android的GUI系统做一个简要但全面的介绍,希望借此能够帮助大家找到上述问题的答案。
所有的内容可以浓缩在下面这张图里,里面有很多的名称和概念我们需要事先解释一下:
1. Window, PhoneWindow 和 Activity
Activity
是 Android 应用的四大组件之一(Activity, Service, Content Provider, Broadcast Receiver
), 也是唯一一个与用户直接交互的组件。Window
在 不同的地方有着不同的含义。在Activity
里,Window
是一个抽象类,代表了一个矩形的不可见的容器,里面布局着若干个可视的区域(View).每个Activity都会有一个Window
类成员变量,mWindow
.而在WindowManagerService
里,Window指的是WindowState
对象,从图中可以看出,WindowState
与一个ViewRootImpl
里的mWindow
对象相对应。所以说,WindowManagerService
里管理的Window其实是Acitivity的ViewRoot
。我们下面提到的Window
,如果没有做特殊说明,均指的是WindowManagerService
里的‘Window’概念,即一个特定的显示区域。从用户角度来看,Android是个多窗口的操作系统,不同尺寸的窗口区域根据尺寸,位置,z-order
及是否透明等参数叠加起来一起并最终呈现给用户。这些窗口既可以是来自一个应用,也可以来自与多个应用,这些窗口既可以显示在一个平面,也可以是不同的平面。总而言之,窗口是有层次的显示区域,每个窗口在底层最终体现为一个个的矩形Buffer
,这些Buffer
经过计算合成为一个新的Buffer
,最终交付Display系统进行显示。为了辅助最后的窗口管理,Android定义了一些不同的窗口类型:- 应用程序窗口 (Application Window): 包括所有应用程序自己创建的窗口,以及在应用起来之前系统负责显示的窗口。
- 子窗口(Sub Window):比如应用自定义的对话框,或者输入法窗口,子窗口必须依附于某个应用窗口(设置相同的token)。
- 系 统窗口(System Window): 系统设计的,不依附于任何应用的窗口,比如说,状态栏(Status Bar),
导航栏(Navigation Bar), 壁纸(Wallpaper), 来电显示窗口(Phone),锁屏窗口(KeyGuard),
信息提示窗口(Toast), 音量调整窗口,鼠标光标等等。
PhoneWindow
是Activity Window的扩展,是为手机或平板设备专门设计的一个窗口布局方案,就像大家在手机上看到的,一个PhoneWindow
的布局大致如下:
2. View, DecorView, ViewGroup, ViewRoot
View
是一个矩形的可见区域。
ViewGroup
是一种特殊的View, 它可以包含其他View并以一定的方式进行布局。Android支持的布局有FrameLayout, LinearLayout, RelativeLayout
等。
DecorView
是FrameLayout
的子类,FrameLayout
也叫单帧布局,是最简单的一种布局,所有的子View
在垂直方向上按照先后顺序依次叠加,如果有重叠部分,后面的View
将会把前面的View
挡住。我们经常看到的弹出框,把后面的窗口挡住一部分,就是用的FrameLayout
布局。Android的窗口基本上用的都是FrameLayout
布局, 所以DecorView
也就是一个Activity Window
的顶级View
, 所有在窗口里显示的View
都是它的子View
.
ViewRoot
. 我们可以定义所有被addView()
调用的View
是ViewRoot
, 因为接口将会生成一个ViewRootImpl
对象,并保存在WindowManagerGlobal
的mRoots[]
数组里。一个程序可能有很多了ViewRoot
(只要多次调用addView()
), 在WindowManagerService
端看来,就是多个Window
。但在Activity
的默认实现里,只有mDecorView
通过addView
添加到WindowManagerService
里( 见如下代码)
//frameworks/base/core/java/android/app/Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
因此一般情况下,我们可以说,一个应用可以有多个Activity
,每个 Activity
一个Window(PhoneWindow)
, 每个Window
有一个DecorView
, 一个ViewRootImpl
, 对应在WindowManagerService
里有一个Window(WindowState)
.
3. ViewRootImple, WindowManagerImpl, WindowManagerGlobals
WindowManagerImpl
: 实现了WindowManager
和 ViewManager
的接口,但大部分是调用WindowManagerGlobals
的接口实现的。
WindowManagerGlobals
: 一个SingleTon
对象,对象里维护了三个数组:
mRoots[ ]
: 存放所有的ViewRootImpl
mViews[ ]
: 存放所有的ViewRoot
mParams[ ]
: 存放所有的LayoutParams
.
同时,它还维护了两个全局IBinder
对象,用于访问WindowManagerService
提供的两套接口:
IWindowManager
: 主要接口是OpenSession()
, 用于在WindowManagerService
内部创建和初始化Session
, 并返回IBinder
对象。ISession
: 是Activity Window
与WindowManagerService
进行对话的主要接口.
ViewRootImpl
:
ViewRootImpl
在整个Android的GUI系统中占据非常重要的位置,如果把Activity
和View
看作 ‘MVC’ 中的V, 把各种后台服务看作Modal
,ViewRootImpl
则是’MVC’ 中的’C’ - Controller. Controller
在MVC架构里承担着承上启下的作用,一般来说它的逻辑最为复杂。从下图可以看到,ViewRootImpl
与 用户输入系统(接收用户按键,触摸屏输入), 窗口系统(复杂窗口的布局,刷新,动画),显示合成系统(包括定时器Choreograph
, SurfaceFlinger
), 乃至Audio
系统(音效输出)等均有密切的关联。研究ViewRootImpl
是研究Android
整个窗口系统的核心和切入点,我们将在后面详细讨论ViewRootImpl
的实现和作用。
三 者( ViewRootImpl, WindowManagerImpl, WindowManagerGlobal
) 都存在于应用(有Activity
)的进程空间里,一个Activity
对应一个WindowManagerImpl
, 一个DecorView(ViewRoot)
,以及一个ViewRootImpl
(上面说过,实际一个Activity
只有一个DecorView
),而WindowManagerGlobals
是一个全局对象,一个应用永远只有一 个。
注意的是,在某些情况下,一个应用可能会有几个ViewRootImpl
对象,比如说ANR
是弹出的对话框,或是网页里面一个视频窗口 (SurfaceView
), 在WindowManagerService
看来,它们也是一个窗口。同时,SystemServer
的进程空间也有自己的 WindowManagerGlobals
和若干个ViewRoot
, 因为WindowManagerService
内部也会管理某些系统窗口,如手机顶部的StatusBar
, 手机底部的NavigationBar
, 以及 锁屏(KeyGuard
)窗口,这些窗口不属于某个特定的Activity
。
4. WindowManager, WindowManagerService
和 WindowManagerPolicyService
WindowManager
: 是一个接口类,定义了一些接口来管理Acitivity
里的窗口。WindowManager
是Android应用进程空间里的一个对象,不提供IPC
服务。
WindowManagerService
: 是SystemServer
进程里的一个Service
,它的主要功能有
- 窗 口的显示刷新。这里的’
Window
’ 其实是ViewRoot
, 和上面WindowManager
管理的’Window
'是不一样的,前者是实实在在要进行显示的‘窗口’,而后者只是一个View
的容器,并不会显示出来。大部分情况下,Android
同时只有一个Activity
工作,但这并不意思着只有一个Window
被显示,Android
可能会同时显示来自相同或不同应用的多个Window
,比如说,屏幕的上方有一个状态栏,最下方有一个导航栏,有时会弹出一些对话框,背景可能会显示墙纸,在应用启动过程中,会有动画效果,这个时候两个Activity
的窗口会有所变形且同时显示出来,这一切都需要WindowManager
来控制何时,何地,以何种方式将所有的窗口整合在一起显示。 - 预处理用户输入时间(
GlobalKey
?SystemKey
), 并分发给合适的窗口进行处理。 - 输出显示(
Display
)管理。包括WifiDisplay
.
WindowManagerService
是Android Framework
里最为庞大复杂的模块之一,我们后面会从各个方面对它进行尽可能详细的分析。
5. Token, WindowToken, AppWindowToken, ApplicationToken, appToken
Token
在英语中表示标记,信物的意思,在代码中,有点类似Handle
,Cookie
, ID
, 用来标识某个特定的对象。在Android的窗口系统中,有很多的’Token
’, 它们代表着不同的含义。
WindowToken
: 是在WindowManagerService
中定义的一个基类,顾名思义,它是用来标识某一个窗口。和下面的appWindowToken
相比, 它不属于某个特定的Activity
, 比如说输入法窗口,状态栏窗口等等。
appWindowToken
: 顾名思义,它是用来标识app
, 跟准确的说法,是用来标识某个具体的Activity
.
ApplicationToken
: 指的是ActivityRecord
类里的Token
子类。appWindowToken
里的appToken
也就是它。
appToken
: 和applicationToken
是一个意思。
下图描绘了各个Token
之间的关系。一个Token
下面带一个WindowList
队列,里面存放着隶属与这个Token
的所有窗口。当一个Window
加入WindowManagerService
管理时,必须指定他的Token
值,WindowManagerService
维护着一个Token
与WindowState
的键值Hash
表。
通过 ‘dumpsys window tokens’ 我们可以列出WindowManagerService
当前所有的Token
和 窗口。比如,
WINDOW MANAGER TOKENS (dumpsys window tokens)
All tokens:
WindowToken{4ea639c4 null}: //token = NULL
windows=[Window{4ea7670c u0 Application Not Responding: jackpal.androidterm}, Window{4ea63a08 u0 Keyguard}]
windowType=-1 hidden=false hasVisible=true
AppWindowToken{4eb29760 token=Token{4eb289d4 ActivityRecord{4ea87a20 u0 com.android.launcher/com.android.launcher2.Launcher}}}: //Launcher2
windows=[Window{4ea837c8 u0 com.android.launcher/com.android.launcher2.Launcher}]
windowType=2 hidden=true hasVisible=true
...
WindowToken{4eb1fd48 android.os.BinderProxy@4eae8a5c}:
windows=[Window{4ea92b78 u0 PopupWindow:4ea0240c}] //对话框
windowType=-1 hidden=false hasVisible=false
AppWindowToken{4eb5d6c0 token=Token{4ea35074 ActivityRecord{4ea68590 u0 jackpal.androidterm/.Term}}}:
windows=[Window{4eb314e4 u0 jackpal.androidterm/jackpal.androidterm.Term}]
windowType=2 hidden=false hasVisible=true
app=true
6. Surface, Layer
和 Canvas, SurfaceFlinger, Region,LayerStack
在Android中,Window
与Surface
一一对应。 如果说Window
关心的是层次和布局,是从设计者角度定义的类,Surface
则从实现角度出发,是工程师关系和考虑的类。Window
的内容是变化 的,Surface
需要有空间来记录每个时刻Window
的内容。在Android的SurfaceFlinger
实现里,通常一个Surface
有两块 Buffer
, 一块用于绘画,一块用于显示,两个Buffer按照固定的频率进行交换,从而实现Window
的动态刷新。
Layer
是SurfaceFlinger
进行合成的基本操作单元。Layer
在应用请求创建Surface
的时候在SurfaceFlinger
内部创建,因此一个Surface
对应一个 Layer
, 但注意,Surface
不一定对应于Window
,Android
中有些Surface
并不跟某个Window
相关,而是有程序直接创建,比如说 StrictMode
, 一块红色的背景,用于提示示Java代码中的一些异常, 还有SurfaceView
, 用于显示有硬件输出的视频内容等。
当多个Layer
进行合成的时候,并不是整个Layer
的空间都会被完全显示,根据这个Layer
最终的显示效果,一个Layer
可以被划分成很多的Region
, Android SurfaceFlinger
定义了以下一些Region
类型:
TransparantRegion
: 完全透明的区域,在它之下的区域将被显示出来。OpaqueRegion
: 完全不透明的区域,是否显示取决于它上面是否有遮挡或是否透明。VisibleRegion
: 可见区域,包括完全不透明无遮挡区域或半透明区域。visibleRegion = Region -above OpaqueRegion
.CoveredRegion
: 被遮挡区域,在它之上,有不透明或半透明区域。DirtyRegion
: 可见部分改变区域,包括新的被遮挡区域,和新的露出区域。
Android 系统支持多种显示设备,比如说,输出到手机屏幕,或者通过WiFi
投射到电视屏幕。Android
用Display
类来表示这样的设备。不是所有的Layer
都会输出到所有的Display
, 比如说,我们可以只将Video Layer
投射到电视, 而非整个屏幕。LayerStack
就是为此设 计,LayerStack
是一个Display
对象的一个数值, 而类Layer
里也有成员变量mLayerStack
, 只有两者的mLayerStack
值相同,Layer
才会被输出到给该Display
设备。所以LayerStack
决定了每个Display
设备上可以显示的Layer
数目。
SurfaceFlinger
的工作内容,就是定期检查所有Layer
的参数更新(LayerStack
等),计算新的DirtyRegion
, 然后将结果推送给底层显示驱动进行显示。这里面有很多的细节,我们将在另外的章节专门研究。
上面描述的几个概念,均是针对于显示这个层面,更多是涉及到中下层模块,应用层并不参与也无需关心。对于应用而言,它关心的是如何将内容画出来。Canvas
是Java层定义的一个类,它对应与Surface
上的某个区域并提供了很多的2D绘制函数(借助于底层的Skia
或OpenGL
)。应用只需通过 LockCanvas()
来获取一个Canvas
对象,并调用它的绘画方法,然后 unLockCanvasAndPost()
来通知底层将更新内容进行显示。当然,并不是所有应用程序都需要直接操作Canva
, 事实上只有少量应用需要直接操作Canvas
, Android提供了很多封装好的控件 Widget
,应用只需提供素材,如文字,图片,属性等等,这些控件会调用Canvas
提供的接口帮用户完成绘制工作。
7. SurfaceFlinger, HWComposer, OpenGL
和 Display
SurfaceFlinger
是一个独立的Service
, 它接收所有Window
的Surface
作为输入,根据ZOrder
, 透明度,大小,位置等参数,计算出每个Surface
在最终合成图像中的位置,然后交由HWComposer
或OpenGL
生成最终的显示Buffer
, 然后显示到特定的显示设备上。
HWComposer
是 Andrid 4.0后推出的新特性,它定义一套HAL
层接口,然后各个芯片厂商根据各种硬件特点来实现。它的主要工作是将SurfaceFlinger
计算好的Layer
的显示参数最终合成到一个显示Buffer
上。注意的是,Surface Flinger
并非是HWComposer
的唯一输入,有的Surface
不由Android的WindowManager
管理,比如说摄像头的预览输入Buffer
, 可以有硬件直接写入,然后作为HWComposer
的输入之一与SurfaceFlinger
的输出做最后的合成。
OpenGL
是一个2D/3D图形库,需要底层硬件(GPU)和驱动的支持。在Android 4.0
后,它取代Skia
成为Android 的2D 绘图图形库,大部分的控件均改用它来实现,应用程序也可以直接调用OpenGl
函数来实现复杂的图形界面。
Display
是Android对输出显示设备的一个抽象,传统的Display
设备是手机上的LCD屏,在Andrid 4.1 后,Android
对SurfaceFlinger
进行了大量的改动,从而支持其他外部输入设备,比如HDMI
, Wifi Display
等等。Display
的输入是根据上面的LayerStack
值进行过滤的所有Window
的Surface
, 输出是和显示设备尺寸相同的Buffer
, 这个Buffer
最终送到了硬件的FB设备,或者HDMI
设备,或者远处的Wifi Display Sink
设备进行显示。输入到输出这条路径上有SurfaceFlinger
, OpenGL
和 HWComposer
。
有了上述概念的解析,对Android的GUI
系统应该有了一些模糊的认识,接下来我们将按下面的顺序将一步步深入其中的细节。