从零开始仿写一个抖音App——Android绘制机制以及Surface家族源码全解析

链接到

本篇文章分为以下章节,读者可以按需阅读

  • 1.Android绘制机制概览
  • 2.Android绘制机制源码分析
  • 3.Surface家族源码全解析
  • 4.总结

阅读须知

  • 1.进入微信公众号 世界上有意思的事 发送消息:Android绘制机制以及Surface家族源码全解析,即可获取本文的 pdf 版。
  • 2.本文分析的源码版本是 Android 7.0,建议结合源码阅读本文
  • 3.推荐一个 Android 源码阅读网站:Android 源码阅读网站
  • 4.因为很多 java 层和 c++ 层的类命名都相同,所以后续例如:Surface.lockCanvas 表示 java 层的调用,Surface::lockCanvas 表示 c++ 层的调用
  • 5.本文的一些缩写:SF——>SurfaceFlinger、WMS——>WindowManagerService、MessageQueue——>MQ、GL——>OpenGL、BQ——>BufferQueue、ST——>SurfaceTexture、TV——>TextureView、SV——>SurfceView
  • 6.本文是视频编辑SDK开发的重要前置知识,建议读透

一、Android绘制机制概览

在深入各种源码之前,我们需要了解 Android 绘制机制的整体架构,以及一些概念

1.Android屏幕刷新机制

从零开始仿写一个抖音App——Android绘制机制以及Surface家族源码全解析_第1张图片

图1:屏幕刷新.jpg

图1就是 Android 屏幕显示的抽象示意图,这里我来解释一下:

  • 1.首先图的横轴是时间,纵轴从下到上分别表示:CPU 处理、GPU 处理、屏幕显示,这三个步骤也就是我们写的代码到图像显示在屏幕上的流程。
  • 2.我们都知道要让手机不卡顿一个显而易见的标准就是:屏幕上每隔一定的 ms 就能显示下一帧图像。在这里这个时间由底层控制,也就是图中两个 VSync 的间隔时间——16ms
  • 3.Android 中引入了下面这些特性来保证屏幕上的数据每隔 16ms 来刷新一次。
    • 1.一个固定的脉冲信号——VSync,这个东西由底层保证,每一次 VSync 信号来了 CPU 就开始运行绘制代码(例如运行 View.draw 之类的方法),当 CPU 的数据准备好了,就将这些数据交给 GPU 让其在一块内存缓冲区上进行图像的绘制。当 GPU 绘制好了就将图像显示到屏幕上。
    • 2.三缓冲,图中的 A、B、C 表示的是三块内存缓冲区。因为不是每次 CPU、GPU 的数据处理都能在16ms 内完成的,所以为了不浪费时间而且不丢弃之前做完的工作。CPU、GPU 的工作会依次反应在 A、B、C 三块内存缓冲区中。而屏幕每次都取当前已经准备好的内存缓冲区。三缓冲较双缓冲的问题就是:我们的操作最终显示到屏幕上的时候会延迟16ms,这可能也是 Android 不如 ios ”跟手“的一个原因
  • 4.由我们可以得出两个简单的结论:
    • 1.ui线程太忙了,使得 CPU 16ms 内没有处理好数据会导致丢帧。
    • 2.需要绘制的图像太复杂,导致 GPU 16ms 没有绘制好图像也会导致丢帧。

2.Android图像绘制方式

问大家一个问题:平时我们开发过程中可以用哪些工具在屏幕上绘制图像?大家一定可以回答出很多东西:View、Drawable、xml、SV、GLSurfaceView、TV、Canvas等等。其实 Android 上面一共只有两种常用的绘图机制,上面列举出来的东西都是由这两种机制演变而来的。这一节我就简单归纳介绍一下。

Android 的两种常用绘图机制:

  • 1.Skia图形库:Skia官网,Skia是一个开源的二维图形库,提供各种常用的API,并可在多种软硬件平台上运行。在没开启硬件加速的时候用到 Canvas 的地方在底层都会调用到 Skia 库中去。在上面我们列举的方式里面:View、Drawable、xml、SV、Canvas 最终使用的都是 Skia 库。另外 Skia 最终是使用 CPU 来对图像进行最终绘制,所以效率比较低。
  • 2.GL:GL 是用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口。在开启硬件加速的时候用到 Canvas 的地方最终会调用到 GL ES 库中。没开启硬件加速的时候上面我们列举的方式中:GLSurfaceView、TV 最终使用的是 GL ES另外 GL 使用的是 GPU 来对图像进行绘制,所以效率比较高。
  • 3.在后续的章节里我会从源代码上分析上面各种绘图方式的调用链。
  • 4.其实在 7.0 之后 Android 中添加了 Vulkan。Vulkan 是用于高性能 3D 图形的低开销、跨平台 API。与 GL ES 一样,Vulkan 提供多种用于在应用中创建高质量的实时图形的工具。不过目前很少用到,所以本篇文章中我们不讨论它。

3.Android绘制中的生产者和消费者

android 的绘制机制中存在着一系列生产者消费者,这一节我将介绍一下这个机制中相关的概念。

  • 1.BQ:如图2所示,BQ 是一个存储着内存块的队列。
    • 1.生产者:它可以使用两个 api,queuedequeue
      • 1.dequeue:需要一块内存来绘制图像的时候,可以从队列的尾部拿出一块内存进行绘制。
      • 2.queue:当图像绘制完毕的时候,可以将该内存添加到队列的头部
    • 2.消费者:它也可以使用两个 api,acquirerelease
      • 1.acquire:当需要一块已经绘制完成的内存再对其进行处理的时候,可以从队列的头部拿出一块内存。
      • 2.release:当对图像内存处理完毕的时候,可以将内存重置然后放回队列的尾部

从零开始仿写一个抖音App——Android绘制机制以及Surface家族源码全解析_第2张图片

图2:BufferQueue.png

  • 2.图像内存的生产者:
    • 1.Surface:Surface 是 BQ 的生产者。当我们使用 lockCanvasunlockCanvasAndPost 的时候,就是先从 BQ 中取出一块内存然后调用 Canvas/GL 的 api 对内存进行绘制,最后将内存放回 BQ 中。
    • 2.我们知道了 Surface 是生产者,那么像 View、SV、GLSurfaceView 这些间接或者直接用到了 Surface 的东西就都是生产者了。
  • 3.图像内存的消费者:
    • 1.SF:它有自己的进程,在 Android 系统启动的时候就像其他各种 Service 一样被创建了。它的作用是接受来自多个来源的内存缓冲区,对它们进行合成,然后发送到显示设备。大多数应用通常在屏幕上有三个层:屏幕顶部的状态栏、底部或侧面的导航栏以及应用的界面。所以应用的 Surface 生产的内存就会被它所消耗。
    • 2.ST:这个东西是常常用在 TV 中。它可以在我们的应用中使用。它在创建的时候会建立一个自己的 BQ。我们可以通过 ST 来创建一个 Surface 然后通过 Surface 向 BQ 中提供图像内存。此时 ST 就可以消耗这些图像内存。它可以使用 GL 来对这些被消耗的图像内存进行二次处理,然后让这些被处理之后的图像内存在通过 GLSurfaceView 之类的东西显示到屏幕上。

你可能感兴趣的:(canvas)