装X的副标题:门外汉视角看我的Android代码是如何显示到你的手机屏幕上的
本文属于科普文,不会涉及到代码,为方便说明会有一些术语。
目的:本文(尽力)从软件层说明用户看到的各种UI在Android系统是如何被组织并显示到屏幕上的。
受众:使用过Android设备,对Android知识有兴趣,但并不打算真的做Android开发的人。也欢迎Android开发人员来挑毛病。
0. 概要
本文依次从View -> Window -> Surface三层说明Android的显示逻辑,另外会简单介绍显示的VSYNC刷新机制。
1. UI元素 - View
Android平台提供了一些用于显示的UI元素。按照能否在其中添加子元素分为两类:
- 比如显示文字的TextView、显示图片的ImageView、按钮Button、多选框CheckBox、开关Switch等。不能在其中添加子元素,一般称为控件widget。
- 还有一些用来组织控件的元素,比如帧布局FrameLayout、线性布局LinearLayout、相对布局RelativeLayout、可滑动容器ScrollView、列表ListView等。这些元素可以在其中添加子元素,可以是第一类也可以是第二类。
虽然分为两类,但是所有的的UI元素均基于同一个基础类View进行扩展,只是第二类是在View的子类ViewGroup的基础上扩展出来的。为了达到复杂或者特殊的效果,可能需要开发人员自行扩展View。
Screenshot_1543282085.png2. UI组织方式 - Window
Android中的每个UI页面是以树的形式来组织这些UI元素,也就是说有一个根元素,然后向下一层层往里填充子元素,xml格式的布局文件也说明了这个逻辑。因为是包含关系,所以父元素的属性会影响到子元素的显示,比如尺寸、透明度等。
开发人员绘制UI页面,就是把各种元素按照期望的UI效果进行组合配置/布局,所以看起来类似的效果,可能每个开发人员对UI元素的组织方式并不相同。
一个一个的UI页面称为Window,系统通过控制Window的显示和隐藏来切换用户看到的功能。
Screenshot_1543149842.png为了实现不同的效果,Android提供的基础Window有:Activity、Dialog和Toast,也允许自定义的Window,比如系统带的状态栏、虚拟导航栏,输入法键盘,还有一些应用做的悬浮图标等。
- Activity是最通用的UI界面,可以实现“全屏”的UI。
- Dialog用来实现非全屏的UI,一般作为辅助UI,用来给用户提示、确认、选择等。
- Toast扩展性比较差,只是用来短暂显示一个提示消息,一定时间后自动消失。
- 自定义的Window,可以通过标准API实现。
备注:
b_snackbar-1080x673.jpgActivity虽说是全屏,但是也可以实现成非全屏的效果。
Google也在提供新的View控件替换Dialog和Toast,因为这两中Window不方便管理生命周期,比如DialogFragment和SnackBar。
3. 绘制 - Surface
不论是Activity、Dialog,还是其他各种自定义Window,都是由Android平台的窗口管理服务WindowManager控制,处理窗口的显示、隐藏、摆放和层叠。
窗口的创建、销毁和刷新整体逻辑是由WindowManager控制。其中UI元素的创建、刷新和销毁逻辑,是树状的视图内部由上向下一级一级自行的处理。
那WindowManager中的这些Window又是如何被绘制呢?
这块有一个概念Surface,Surface可以认为是一张内存画布,用来“绘制”Window的UI。Surface由Android的另一个框架服务SurfaceFlinger管理。
每个Window关联一个Surface,Window的UI被绘制在Surface上,SurfaceFlinger把当前可见的Surface(不同Window可见的部分)进行合并,合并为一屏幕(一层)的内容。然后这一屏内容被SurfaceFlinger通过显示驱动绘制到图形芯片/显卡的存储,这个绘制是由CPU和GPU完成的。
Window -> Surface
WindowManager -> SurfaceFlinger
Android也允许应用不用普通View,直接在Surface上绘制(SurfaceView...)
Screenshot_1547378948.png4. 刷新 - VSync(垂直同步信号量)
至此我们得到了一帧的图像,那手机屏幕连续的变化 - 视频是如何显示的呢?
老生常谈:视频是由称为“帧”的单个图片,平滑动画通常为每秒60帧。帧由像素组成,当显示器绘制帧时,逐行填充像素。
VSYNC.png理想情况下,显示器在完成绘制前一帧后从图形芯片/显卡获取下一帧数据,并逐行绘制。显示器的更新频率是稳定的,CPU和GPU绘制的时间是不固定的。当在LCD绘图过程中产生了一个新帧时,即屏幕刷新和显卡数据刷新不同步,就会出现撕裂,显示一帧的一部分,另一帧的一部分。
VSync是一个同步的东西,就是用来保证屏幕和数据的刷新同步。屏幕每次刷新之前就会发送一个信号告诉系统,现在准备刷新了,然后系统再去调用CPU和GPU进行UI更新,得到一帧新的数据进行显示。
Android一直使用VSync来防止屏幕撕裂,大多数Android显示器运行在60帧/秒左右(60Hz)。为了有一个平滑的感受,必须实际上能够处理每秒60帧 - 这意味着有16毫秒来处理每一帧。如果花费的时间超过16ms,就会卡顿。
总结
整体逻辑:每次屏幕定时刷新时,发出VSync信号,当前可见的Window调用内部视图树把UI元素“绘制”到跟Window绑定的Surface,SurfaceFlinger把所有窗口可见的部分Surface内容合成一屏内容,发送到图形芯片的存储,显示器从存储中获取这样的一帧数据进行显示。
网友评论