Skip to content

Latest commit

 

History

History
112 lines (47 loc) · 10.8 KB

6.屏幕绘制基础.md

File metadata and controls

112 lines (47 loc) · 10.8 KB

6.屏幕绘制基础

Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口中进行具体的视图内容绘制;对于每个客户端而言,他们都感觉自己独占了屏幕,而对于服务端而言,它会给每一个客户端窗口分配不同的层值,并根据用户的交互情况动态改变窗口的层值,这就给用户造成了所谓的前台窗口和后台窗口的概念;

Android的屏幕绘制架构如下图:

  • SurfaceFlinger服务进程:简称sf。该进程在整个系统中只有一个实例,在系统开机后自动运行,它的作用是给每个客户端分配窗口,程序中用Surface类表示这个窗口。正如Surface字面的意义,它是一个平面,即每个窗口是一个平面,每个平面在程序中都对应一段内存,也就是所谓的屏幕缓冲区,不同窗口的缓冲区大小不同,这取决于该窗口的大小,即宽度和高度,一般来讲缓冲区的大小为宽度x高度。sf的客户端必须使用SurfaceFlinger的客户端接口驱动来和sf打交道,系统中使用该接口驱动的最重要的进程就是SystemServer进程。
  • 当一个APK程序需要创建窗口时,会使用WindowManager类的addView()函数,该函数会创建一个ViewRoot(ViewRoot类在Android2.2之后就被ViewRootImpl替换了,这里因为方便后面还是会叫做ViewRoot)对象,而ViewRoot类中会使用Surface的无参构造函数创建一个Surface对象,此时该Surface仅仅是一个空壳,然后调用WindowManager类向WmS服务发起一个请求,请求的参数中包含该Surface对象。虽然它表示的是一个窗口,但它必须经过初始化后才真正能够对应一个再屏幕上显示的窗口,而初始化的本质就是给该Surface对象分配一段屏幕缓冲区的内存。
  • WmS收到这个请求后,会通过Surface类的JNI调用到SurfaceFlinger_client驱动,通过该client接口驱动请求sf进程创建指定的窗口。于是sf创建一段屏幕缓冲区,并在sf内部记录该窗口,然后sf会把该窗口的缓冲区地址传递给WmS,WmS再用这个地址去初始化APK程序传入的Surface对象,并最终回到APK程序中。此时APK程序中的Surface对象是一个真正的Surface对象了,因为它包含的屏幕缓冲区已经由sf创建并备案了。
  • APK程序有了这个Surface后,就可以给这个平面上绘制任意的内容了,比如绘制矩阵、绘制文本、绘制图片等。然后Surface类本质上仅仅表示了一个平面,而绘制不同图片显然是一种操作,而不是一段数据,因此Android中使用了一个叫做Skia的绘图驱动库,该库使用C/C++语言编写而成,其作用就是能够进行各种平面绘制。在程序中用Canvas类来表示这个功能对象,Canvas类有很多绘制函数,比如drawColor()、drawLine()等。Surface类包含了一个函数lockCanvas(),APK应用程序可以通过该函数返回一个Canvas功能对象,然后就可以调用该对象的各种绘制函数完成对平面的绘制。

相关类

  • Surface类:该类用于描述一个绘制平面,其内部仅仅包含了该平面的大小,在屏幕上的位置,以及一段屏幕缓冲内存区。不过在Java端,不能直接访问这段内存,同时也不能通过该类直接设置平面的大小和位置,而只能通过SurfaceHolder类。

    一般情况下,客户端程序对应的Surface都是由底层的ViewRoot类进行创建的,而ViewRoot中创建Surface的函数在SDK中没有开放,所以应用程序不能通过ViewRoot类直接创建Surface对象,而只能通过SurfaceView类间接创建。

    对于SDK开发的APK程序而言,不能直接创建Surface对象,而只能使用WindowManager类的addView()方法创建一个窗口,因为Surface的构造函数都是@hide的,即不会出现在SDK中。Surface类有两个构造函数:

    • public Surface() {}

      使用该构造函数创建的Surface对象仅仅是一个空壳,因为每个Surface内部都会对应一段屏幕缓冲区内存,对于空壳子Surface而言,这段内存不存在。

    • public Surface(SurfaceSession s, int pid, int display, int w, int h, int format, int flags)

      该构造函数包含窗口大小相关的参数,使用该构造函数会创建一个真正的Surface对象。

    WindowManager类的addView()函数会创建一个ViewRoot对象,而ViewRoot类则使用Surface的无参数构造函数创建了一个Surface对象,此时该Surface是一个空壳,然后ViewRoot类调用WmS中的IWindowSession服务为该Surface对象分配真正的屏幕缓冲区内容。之后Surface的构造函数会去创建一个Canvas对象,然后调用init()函数来初始化该Surface对象,该函数内部会首先获得一个SurfaceComposerClient对象,该类正是native层面上的SurfaceFlinger服务的客户端对象,然后调用该对象的createSurface()函数创建一个真正的Surface对象。之后调用Surface的lock()函数获取一个SurfaceInfo对象。该SurfaceInfo对象中获取该Surface对应屏幕缓冲区内存地址。再用这个地址构造一个SkBitmap对象,之后用SkBitmap对象构造一个SkCanvas对象,这个SkCanvas对象是底层真正进行绘制的功能类对象,Java层面的Canvas类仅仅是该类的包装而已,之后将Canvas对象返回到Java端。

  • Canvas类:是一个功能类,该类包含各种绘制函数,例如drawColor()、drawLine()等。构造Canvas对象时,必须为该Canvas指定一段内存地址,因为绘制的结果实际上就是给这段内存地址中填充不同的像素值。这段内存有两种类型,一种是普通的内存,另一种是屏幕缓冲区内存,当Canvas对应的内存为屏幕缓冲区内存时,绘制函数执行后就可以在屏幕上看到,如果是一段普通内存,则不会在屏幕上看到,不过却可以将这段内存复制到拥有屏幕缓冲内存的Canvas中,这种方式就是游戏开发中常用的方式。

  • Drawable类:是一个抽象类,该类是一个功能类,但它与Canvas的相同之处是两者可以给内存缓冲区中绘制图案,两者的区别有两点:

    • Drawable类内部不存在一段内存缓冲区,当应用程序需要绘制某种图案时,可以将一个包含内存缓冲区的Canvas对象传递给Drawable,然后Drawable就可以给该Canvas上绘制相应的团。
    • 每个具体的Drawable对象仅仅绘制某个特定的图案,SDK中包含的Drawable实现类有BitmapDrawable、NinePatchDrawable等。

Linux内核提供了统一的framebuffer显示驱动。设备节点/dev/graphics/fb或者/dev/fb,其中fb0表示第一个Monitor,当前系统中只用到了一个显示屏。

Android的HAL层提供了Gralloc,包括fb和gralloc两个设备。前者负责打开内核中的framebuffer、初始化配置,并提供了post、setSwapInterval等操作接口。后者则管理帧缓冲区的分配和释放。这就意味着上层元素只能通过Gralloc来间接访问帧缓冲区,从而保证了系统对framebuffer的有序使用和统一管理。

另外,HAL层的另一重要模块是“Composer”,如其名所示,它为厂商自定制“UI合成”提供了接口。Composer的直接使用者是SurfaceFlinger中的HWComposer,后者除了管理Composer的HAL模块外,还负责VSync信号的产生和控制。VSync则是Project Butter工程中加入的一种同步机制,它既可以由硬件产生,也可以通过软件来moni(VsyncThread)。

Framebuffer是系内核系统提供的图形硬件的抽象描述。之所以称为buffer,是因为它也占用了系统存储空间的一部分,是一块包含屏幕显示信息的缓冲区。由此可见,在一切都是文件的Linux系统中,Framebuffer被看成了终端显示设备的化身。

另外,Framebuffer借助于Linux文件系统向上层应用提供了统一而高效的操作接口,从而让用户空间中运行的程序可以在不做太多修改的情况下去适配多种显示设备,无论它们属于哪家厂商、什么型号,都由Framebuffer内部来兼容。

在Android系统的GUI设计理念中,提供了两种本地窗口:

  • 面向管理者(SurfaceFlinger)

    既然SurfaceFlinger扮演了系统中所有UI界面的管理者,那么它无可厚非需要直接或间接地持有“本地窗口。这个窗口就是FramebufferNativeWindow。

  • 面向应用程序

    这类本地窗口是Surface。

无论开发者使用什么渲染 API,一切内容都会渲染到“Surface”。Surface 表示缓冲队列中的生产方,而缓冲队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到显示部分。

图像流生产方

图像流生产方可以是生成图形缓冲区以供消耗的任何内容。例如 OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。

图像流消耗方

图像流的最常见消耗方是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用窗口管理器中提供的信息将它们合成到显示部分。SurfaceFlinger 是可以修改所显示部分内容的唯一服务。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface。

其他 OpenGL ES 应用也可以消耗图像流,例如相机应用会消耗相机预览图像流。非 GL 应用也可以是使用方,例如 ImageReader 类。

硬件混合渲染器

显示子系统的硬件抽象实现。SurfaceFlinger 可以将某些合成工作委托给 Hardware Composer,以分担 OpenGL 和 GPU 上的工作量。SurfaceFlinger 只是充当另一个 OpenGL ES 客户端。因此,在 SurfaceFlinger 将一个或两个缓冲区合成到第三个缓冲区中的过程中,它会使用 OpenGL ES。这样使合成的功耗比通过 GPU 执行所有计算更低。

Hardware Composer HAL 则进行另一半的工作,并且是所有 Android 图形渲染的核心。Hardware Composer 必须支持事件,其中之一是 VSYNC(另一个是支持即插即用 HDMI 的热插拔)。