装X的副标题:门外汉视角看我的Android代码是如何显示到你的手机屏幕上的
本文属于科普文,不会涉及到代码,为方便说明会有一些术语。

目的:本文(尽力)从软件层说明用户看到的各种UI在Android系统是如何被组织并显示到屏幕上的。

受众:使用过Android设备,对Android知识有兴趣,但并不打算真的做Android开发的人。也欢迎Android开发人员来挑毛病。

0. 概要

本文依次从View -> Window -> Surface三层说明Android的显示逻辑,另外会简单介绍显示的VSYNC刷新机制。

1. UI元素 - View

Android平台提供了一些用于显示的UI元素。按照能否在其中添加子元素分为两类:

  1. 比如显示文字的TextView、显示图片的ImageView、按钮Button、多选框CheckBox、开关Switch等。不能在其中添加子元素,一般称为控件widget。
  2. 还有一些用来组织控件的元素,比如帧布局FrameLayout、线性布局LinearLayout、相对布局RelativeLayout、可滑动容器ScrollView、列表ListView等。这些元素可以在其中添加子元素,可以是第一类也可以是第二类。

虽然分为两类,但是所有的的UI元素均基于同一个基础类View进行扩展,只是第二类是在View的子类ViewGroup的基础上扩展出来的。为了达到复杂或者特殊的效果,可能需要开发人员自行扩展View。

Screenshot_1543282085.png

2. 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实现。

备注

Activity虽说是全屏,但是也可以实现成非全屏的效果。
Google也在提供新的View控件替换Dialog和Toast,因为这两中Window不方便管理生命周期,比如DialogFragment和SnackBar。

b_snackbar-1080x673.jpg

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.png

4. 刷新 - 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内容合成一屏内容,发送到图形芯片的存储,显示器从存储中获取这样的一帧数据进行显示。

更多相关文章

  1. android 输入法(包括手写界面)
  2. Kotlin Android(安卓)UI利器之Anko Layouts
  3. Android(安卓)自定义数字键盘(一)
  4. (Android) 单击屏幕事件和滑动屏幕事件共存的解决方案
  5. Android实现登陆界面动画
  6. Android:谈一谈安卓应用中的Toast情节(基础)
  7. 【Android(安卓)UI设计与开发】第08期:底部菜单栏(三)Fragment+Frag
  8. Android横竖屏解析
  9. Android仿游戏答题

随机推荐

  1. Android之模仿微信登陆界面(二)
  2. Android 高仿QQ 登陆界面
  3. Android Shareperferences使用
  4. Android 坐标系统
  5. Android两种 旋转Bitmap方法
  6. Android Bluetooth Stream Non-blocking
  7. Android 程序优化
  8. ANDROID NDK makefile 链接静态库的方法
  9. android Studio导入source文件
  10. Android UI 开源组件