写点什么

2021Android 常见笔试题,吐血整理

用户头像
极客开源
关注
发布于: 刚刚

二、显示系统基础知识

在一个典型的显示系统中,一般包括 CPU、GPU、Display 三个部分, CPU 负责计算帧数据,把计算好的数据交给 GPU,GPU 会对图形数据进行渲染,渲染好后放到 buffer(图像缓冲区)里存起来,然后 Display(屏幕或显示器)负责把 buffer 里的数据呈现到屏幕上。如下图:


2.1 基础概念

  • 屏幕刷新频率 一秒内屏幕刷新的次数(一秒内显示了多少帧的图像),单位 Hz(赫兹),如常见的 60 Hz。刷新频率取决于硬件的固定参数(不会变的)。

  • 逐行扫描 显示器并不是一次性将画面显示到屏幕上,而是从左到右边,从上到下逐行扫描,顺序显示整屏的一个个像素点,不过这一过程快到人眼无法察觉到变化。以 60 Hz 刷新率的屏幕为例,这一过程即 1000 / 60 ≈ 16ms。

  • 帧率 (Frame Rate) 表示 GPU 在一秒内绘制操作的帧数,单位 fps。例如在电影界采用 24 帧的速度足够使画面运行的非常流畅。而 Android 系统则采用更加流程的 60 fps,即每秒钟 GPU 最多绘制 60 帧画面。帧率是动态变化的,例如当画面静止时,GPU 是没有绘制操作的,屏幕刷新的还是 buffer 中的数据,即 GPU 最后操作的帧数据。

  • 画面撕裂(tearing) 一个屏幕内的数据来自 2 个不同的帧,画面会出现撕裂感,如下图


2.2 双缓存

2.2.1 画面撕裂 原因

屏幕刷新频是固定的,比如每 16.6ms 从 buffer 取数据显示完一帧,理想情况下帧率和刷新频率保持一致,即每绘制完成一帧,显示器显示一帧。但是 CPU/GPU 写数据是不可控的,所以会出现 buffer 里有些数据根本没显示出来就被重写了,即 buffer 里的数据可能是来自不同的帧的, 当屏幕刷新时,此时它并不知道 buffer 的状态,因此从 buffer 抓取的帧并不是完整的一帧画面,即出现画面撕裂。


简单说就是 Display 在显示的过程中,buffer 内数据被 CPU/GPU 修改,导致画面撕裂。

2.2.2 双缓存

那咋解决画面撕裂呢? 答案是使用 双缓存。


由于图像绘制和屏幕读取 使用的是同个 buffer,所以屏幕刷新时可能读取到的是不完整的一帧画面。


双缓存,让绘制和显示器拥有各自的 buffer:GPU 始终将完成的一帧图像数据写入到 Back Buffer,而显示器使用 Frame Buffer,当屏幕刷新时,Frame Buffer 并不会发生变化,当 Back buffer 准备就绪后,它们才进行交换。如下图:


2.2.3 VSync

问题又来了:什么时候进行两个 buffer 的交换呢?


假如是 Back buffer 准备完成一帧数据以后就进行,那么如果此时屏幕还没有完整显示上一帧内容的话,肯定是会出问题的。看来只能是等到屏幕处理完一帧数据后,才可以执行这一操作了。


当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时有一段时间空隙,称为 VerticalBlanking Interval(VBI)。那,这个时间点就是我们进行缓冲区交换的最佳时间。因为此时屏幕没有在刷新,也就避免了交换过程中出现 screen tearing 的状况。


VSync(垂直同步)是 VerticalSynchronization 的简写,它利用 VBI 时期出现的 vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。另外,交换是指各自的内存地址,可以认为该操作是瞬间完成。


所以说 V-sync 这个概念并不是 Google 首创的,它在早年的 PC 机领域就已经出现了。

三、Android 屏幕刷新机制

3.1 Android4.1 之前的问题

具体到 Android 中,在 Android4.1 之前,屏幕刷新也遵循 上面介绍的 双缓存+VSync 机制。如下图:



以时间的顺序来看下将会发生的过程:


  1. Display 显示第 0 帧数据,此时 CPU 和 GPU 渲染第 1 帧画面,且在 Display 显示下一帧前完成

  2. 因为渲染及时,Display 在第 0 帧显示完成后,也就是第 1 个 VSync 后,缓存进行交换,然后正常显示第 1 帧

  3. 接着第 2 帧开始处理,是直到第 2 个 VSync 快来前才开始处理的。

  4. 第 2 个 VSync 来时,由于第 2 帧数据还没有准备就绪,缓存没有交换,显示的还是第 1 帧。这种情况被 Android 开发组命名为“Jank”,即发生了丢帧

  5. 当第 2 帧数据准备完成后,它并不会马上被显示,而是要等待下一个 VSync 进行缓存交换再显示。


所以总的来说,就是屏幕平白无故地多显示了一次第 1 帧。


原因是 第 2 帧的 CPU/GPU 计算 没能在 VSync 信号到来前完成 。


我们知道,双缓存的交换 是在 Vsyn 到来时进行,交换后屏幕会取 Frame buffer 内的新数据,而实际 此时的 Back buffer 就可以供 GPU 准备下一帧数据了。 如果 Vsyn 到来时 CPU/GPU 就开始操作的话,是有完整的 16.6ms 的,这样应该会基本避免 jank 的出现了(除非 CPU/GPU 计算超过了 16.6ms)。 那如何让 CPU/GPU 计算在 Vsyn 到来时进行呢?

3.2 drawing with VSync

为了优化显示性能,Google 在 Android 4.1 系统中对 Android Display 系统进行了重构,实现了 Project Butter(黄油工程):系统在收到 VSync pulse 后,将马上开始下一帧的渲染。即一旦收到 VSync 通知(16ms 触发一次),CPU 和 GPU 才立刻开始计算然后把数据写入 buffer。如下图:



CPU/GPU 根据 VSYNC 信号同步处理数据,可以让 CPU/GPU 有完整的 16ms 时间来处理数据,减少了 jank。


一句话总结,VSync 同步使得 CPU/GPU 充分利用了 16.6ms 时间,减少 jank。


问题又来了,如果界面比较复杂,CPU/GPU 的处理时间较长 超过了 16.6ms 呢?如下图:



  1. 在第二个时间段内,但却因 GPU 还在处理 B 帧,缓存没能交换,导致 A 帧被重复显示。

  2. 而 B 完成后,又因为缺乏 VSync pulse 信号,它只能等待下一个 signal 的来临。于是在这一过程中,有一大段时间是被浪费的。

  3. 当下一个 VSync 出现时,CPU/GPU 马上执行操作(A 帧),且缓存交换,相应的显示屏对应的就是 B。这时看起来就是正常的。只不过由于执行时间仍然超过 16ms,导致下一次应该执行的缓冲区交换又被推迟了——如此循环反复,便出现了越来越多的“Jank”。


为什么 CPU 不能在第二个 16ms 处理绘制工作呢?


原因是只有两个 buffer,Back buffer 正在被 GPU 用来处理 B 帧的数据, Frame buffer 的内容用于 Display 的显示,这样两个 buffer 都被占用,CPU 则无法准备下一帧的数据。 那么,如果再提供一个 buffer,CPU、GPU 和显示设备都能使用各自的 buffer 工作,互不影响。

3.3 三缓存

三缓存就是在双缓冲机制基础上增加了一个 Graphic Buffer 缓冲区,这样可以最大限度的利用空闲时间,带来的坏处是多使用的一个 Graphic Buffer 所占用的内存。



  1. 第一个 Jank,是不可避免的。但是在第二个 16ms 时间段,CPU/GPU 使用 第三个 Buffer 完成 C 帧的计算,虽然还是会多显示一次 A 帧,但后续显示就比较顺畅了,有效避免 Jank 的进一步加剧。

  2. 注意在第 3 段中,A 帧的计算已完成,但是在第 4 个 vsync 来的时候才显示,如果是双缓冲,那在第三个 vynsc 就可以显示了。


三缓冲有效利用了等待 vysnc 的时间,减少了 jank,但是带来了延迟。 所以,是不是 Buffer 越多越好呢?这个是否定的,Buffer 正常还是两个,当出现 Jank 后三个足以。


以上就是 Android 屏幕刷新的原理了。

总结

【Android 详细知识点思维脑图(技能树)】



其实 Android 开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。


虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。


这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司 19 年的面试题。把技术点整理成了视频和 PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。


由于篇幅有限,这里以图片的形式给大家展示一小部分。



网上学习 Android 的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。


本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

用户头像

极客开源

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
2021Android常见笔试题,吐血整理