Andrioid GUI 系统

0 概述

Android的GUI系统是Android重要也是最复杂的系统之一,主要包括:

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


  1. 窗口和图形系统——Window and View Manager System
  2. 显示合成系统—— SurfaceFlinger
  3. 用户输入系统 ——InputManager System
  4. 应用框架系统——Activity Manager System

他们之间的关系如图


只有对这些系统的功能和工作原理有一个基本的了解,才能解答下面的问题:

  1. Activity启动过程?Activity的onXXX()在后台都做了什么?Activity的show()和hide()是如何控制的
  2. Surface是什么时候被创建的?
  3. Android是如何将一个个的控件画到Surface上的?然后显示到手机屏幕上的?
  4. Android是一个多窗口的系统么?
  5. 应用程序窗口是如何获取焦点的?用户的按键是怎么传递到当前的窗口的?
  6. Android是怎么支持多屏幕互动的(WifiDisplay
  7. 输入法窗口到底输入哪个进程?为什么不管什么应用,只要有输入框的时候都能弹出它?
本文将从框架和流程角度出发,对Android的GUI系统做一个简单全面的介绍,首先来解释一些概念


1 Window,PhoneWindow和Activity

Activity是Android应用的四大组件之一(Activity,Service,Content Provider,BroadCast Receiver),也是唯一一个与用户交互的组件
Window在不同的地方有不同的含义。
  在Activity中,Window是一个抽象类,代表一个 矩形的不可见的容器 ,里面有若干个可见的区域(View),每个Activity都会有一个Window类成员变量- mWindow。
而在WindowManagerService里管理的Window其实是Activity的ViewRoot (下面提及到的没有说明的都是这里的Window,即一个特定的显示区域)
从用户的角度来看,Android是多窗口的操作系统,不同尺寸的窗口区域根据尺寸,位置,z-order及alpha等叠加起来呈现给用户的。这些窗口可以来自于一个应用也可以是多个应用,这些窗口可以显示在一个平面也可以显示在不同的平面。总的来说就是窗口是有层次的显示区域,每个窗口在底层最终体现为一个个矩形的Buffer,这些Buffer经过计算合成为一个新的Buffer,最终交给Display系统进行显示。
为了辅助最后的窗口管理,Android定义了些不同的窗口类型:
1)应用程序窗口(Application Window):包括所有应用程序自己创建的窗口,以及在应用起来之前系统负责显示的窗口。
2)子窗口(Sub Window):比如应用程序自定义的对话框,输入法窗口,子窗口必须依附于某个应用窗口(设置相同的token)
3)系统窗口(System Window):系统设计的,不依附于任何应用程序的窗口,如状态栏(Status Bar),导航栏(Navigation Bar),壁纸(Wallpaper),来电显示窗口(Phone),锁屏窗口(KeyGuard),信息提示窗口(Toast),音量调节窗口,鼠标光标等等。
4)PhoneWindow是Activity Window的扩展,是为手机或平板设备专门设计的一个窗口布局方案,就想大家在手机上看到的,一个PhoneWindow的布局大致如下:

2 View,DecorView,ViewGroup,ViewRoot

View是一个矩形的可见区域。
ViewGroup是一种特殊的View,它可以包含其它的View并以一定的方式进行布局。Android支持LinearLayout,FrameLayout,RelativeLayout等。
DecorView是FrameLayout的子类,FrameLayout也叫单帧布局,是最简单的一种布局,所有的子View在垂直方向上按照先后顺序依次叠加,如果有重叠,后面的View将会将前面的View遮挡。我们平时看到的弹出框就是用的FrameLayout布局。Android上的窗口基本上用的都是FrameLayout布局,所以DecorView也就是ActivityWindow的顶级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.<strong>addView</strong>(mDecor, getWindow().getAttributes());//mDecor通过WindowManager的addView添加        
         mWindowAdded = true;       
    }        
    mDecor.setVisibility(View.VISIBLE);  
}
因此,一般情况下,一个应用程序可以有多个Activity,每个Activity一个Window(PhoneWindow),每个Window有一个DecorView,一个ViewRootImpl,对应在WindowManagerService里有一个Window(WindowState)

3 ViewRootImpl,WindowManagerImpl,WindowManagerGlobals

WindowManagerImpl:实现了WindowManager和ViewManager的接口,但是大部分是调用WindowManagerGlobals的接口实现的。
WindowManagerGlobals:一个singleton对象,对象里有三个数组:
mRoot[]:存放所有的ViewRootImpl
mViews[]:存放所有的ViewRoot
mParams[]:存放所有的LayoutParams
同时,它还维护了两个全局的Binder对象,用于访问WindowManagerService提供的两套接口:
IWindowManager:主要接口是OpenSession(),用于在WindowManagerService内部创建和初始化Session,并返回IBinder对象
ISession:是Activity Window与WindowManagerService进行对话的主要接口

ViewRootImpl:
ViewRootImpl在整个Android的GUI系统中占据非常重要的位置,如果把Activity和View看做是‘MVC’中的‘V’,把各种后台服务看做Modal,ViewRootImpl就是‘C ’,承担着承上启下的作用。从下图可以看到,ViewRootImpl与用户输入系统,窗口系统,显示合成系统(choreograph,surfaceflinger)以及audio系统等都有密切的关联。研究ViewRootImpl就是研究Android整个窗口系统的核心和切入点


ViewRootImpl,WindowManagerImpl,WidnowManagerGlobals都存在于应用(有Activity)的进程空间中,一个Activity对应一个WindowManagerImpl,一个Decorview(ViewRoot),以及一个ViewRootImpl,而WindowManagerGlobals是一个全局对象,一个应用只有一个。
注意:在某些情况下,一个应用程序会有几个ViewRootImpl对象,比如弹出对话框,显示视频窗口(SurfaceView),在WindowManagerService看来,他们也是一个窗口。同时,systemserver的进程空间也有自己的WindowManagerGlobals和若干个ViewRoot,因为WindowManagerService内部也会管理某些系统窗口,如手机顶部的StatusBar,锁屏窗口等,这些不属于某个特定的Activity。

4 WindowManager,WindowManagerService,WindowManagerPolicyService

WindowManager:是一个接口类,定义了一些接口来管理Activity里的窗口。WindowManager是Android应用进程空间里的对象,不提供IPC服务。
WindowManagerService:是SystemService进程的一个Service,主要功能有:
1)窗口的显示刷新(这里的Window是ViewRoot,实实在在的显示窗口 ),通常情况下,Android同时只有一个Activity工作,但这并不意味着只有一个Window被显示,Android可能会同时会显示来自相同或不同应用程序的多个Window,比如说,StatusBar,NavigateBar,开机动画中的两个Activity的窗口同时显示出来,这些都需要WindowManager的控制何时何地以何种方式将所有的窗口整合在一起显示。
2)预处理用户输入时间并分发给合适的窗口进行处理
3)输出显示(Display)管理,包括WifiDisplay
WindowManagerServcie是Android Framework最庞大的而复杂的模块之一

5 Token,WindowToken,ApplicationWindowToken

Token是标记的意思,在代码中,有点像Handle,Cookie,ID,用来标识某个特定的对象。在Android的窗口系统中,有很多的Token,他们代表着不同的含义,如下
WindowToken:是在WindowManagerService中定义的一个基类,用来标识某一个窗口。和下面的appWindowToken相比,它不属于某个特定的Activity,比如输入法窗口,状态栏窗口等等。
appWidnowToken:标识app,是用来标识某个具体的Activity
ApplicationToken:指的是ActivityRecord类里的Token子类,appWindowToken里的token指的就是它
appToken:和ApplicationToken 是一个意思
下图描述了各个Token之间的关系,一个token下面带一个windowlist队列,里面存放着隶属于这个token的所有window。当一个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

Android中,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类型
TransparentRegion:完全透明的区域,在它之下的区域被显示出来
OpaqueRegion:完全不透明的区域,是否显示取决于它上面是否有遮挡或透明
VisibleRegion:可见区域,包括完全不透明无遮挡区域或半透明区域。visibleRegion = Region - above opaqueregion
CoveredRegion:被遮挡的区域,在它之上,有不透明或不透明区域
DirtyRegion:可见部分改变区域,包括新的遮挡区域和新的漏出区域

Android系统支持多中显示设备,如可以通过wifi投射到电视上显示。android用Display类来表示这样的设备。不是所有的layer都会输出到所有的Display,比如说,我们可以将video layer投射到电视,而非整个屏幕。layerstack就是为此设计的,layerstack是一个display对象的一个数值,而类layer里也有的成员变量mLayerStack,只有两者的layerstack值相同,layer才会被输出到该display设备上,所以layerstack决定了每个display设备上可以显示的layer数目。

surfaceflinger的工作内容,就是定期的检查所有的layer参数的更新(如layerstack,alpha等),计算新的DirtyRegion,然后将结果推送给底层显示驱动进行显示。

对于应用而言,它只关心如何将内容画出来,Canvas是java层定义的一个类,它对应于surface上的某个区域并提供很多的2d绘制函数(skia或opengl)。应用程序只需要通过lockcanvas()来获取一个canvas对象,并调用它的draw方法,然后unLockCanvasAndPost()来通知底层将更新内容进行显示。当然,并不是所有的应用程序都是直接操作Canvas,事实上,android封装了很多的Widget,应用程序只需要提供素材就可以通过这些控件调用Canvas提供的接口进行绘制。

7 SurfaceFlinger,HWComposer,OpenGL,Display

SurfaceFlinger是一个独立的Service,它接受所有Window的Surface作为输入,根据z-order,alpha,size,position等计算出每个surface在最终合成图像中的位置,然后交给HWComposer或OpenGL生成最终的显示Buffer,然后显示到特定的显示设备上。

HWComposer是Android 4.0后推出的,它定义了一套HAL层接口,然后哥哥芯片厂商根据各种硬件特点来实现。它的主要工作是将SurfaceFlinger计算好的layer的显示参数最终合成一个显示buffer上,SurfaceFlinger并非是HWComposer的唯一输入,所有的Surface不由Android的WindowManager管理,比如摄像头的预览输入buffer,可以有硬件直接写入,然后作为HWComposer的输入之一与SurfaceFlinger的输出做最后的合成。

OpenGL是一个2D/3D图形库,需要底层硬件(GPU)和驱动的支持,在Android 4.0后,它取代skia成为android的2d回执图形库,大部分的控件都改成用它来实现,应用程序也可以直接调用OpenGL函数实现复杂的图形界面。

Display是android对输出显示设备的一个抽象,传统的display就是手机上的lcd屏,在4.1后,surfaceflinger做了很大的改动,从而能够支持其他外部输入设备,如hdmi,wifidisplay等,display的输入时根据上面的layerstack值进行过滤的所有window的surface,输出是和显示设备尺寸相同的buffer,这个buffer最终送到了硬件的fb设备,或者hdmi设备,或者远处的wifidisplay sink设备进行显示。输入到输出这条路径上有surfaceflinger,opengl,HWComposer。






访问 Android 内置的 Linux 命令行 shell。 受欢迎的“Android Terminal Emulator”的一个新版本。同样的程序,新的名字。 主要功能 + 完整的 Linux 终端模拟器。 + 多窗口。 + 启动器快捷显示 + 支持 UTF-8 文本。(阿拉伯语、中文、希腊语、希伯来文、日语、韩文、俄语、泰文等。) + 全免费,无广告、无内置付费项目,无烦人的屏幕。 常见问题简述: + 如果遇到输入问题,请安装免费的“Hacker's Keyboard IME”。(尤其是对于三星或 HTC 设备。) + 该应用非游戏模拟器。 + 该应用不用于获取手机 root 权限或更改手机 IMEI 码。 + 需知道(或愿意学习)通过该应用使用命令行。 + 为了使用 Android 设备内置命令之外的命令,需安装一组命令行应用,比如 Busy Box 或 Debian chroot。 + 在版本低于 5.0 的 Android 系统上安装该应用,如果出现 -505 错误,表示其他应用正在使用同样的权限。为了成功安装本应用,请先卸载这些应用。(有用户反馈称,导致该问题的原因是 jrummy's Toolbox Pro。) 想要了解更多有关 Terminal Emulator for Android 的信息? 请加入 G+ 社区:#Android Terminal Emulator https://plus.google.com/u/0/communities/106164413936367578283 或查看文档 wiki: http://github.com/jackpal/Android-Terminal-Emulator/wiki 想要参加 Terminal Emulator for Android 的翻译或校对?请访问 https://github.com/jackpal/Android-Terminal-Emulator/wiki/Translating-to-Other-Languages for instructions。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值