图形显示架构

显示框架

从APP启动到界面的显示,Android是如何将应用的画面显示出来的呢?了解Android内部的显示框架,对于我们理解Flutter这类跨平台技术的实现很有意义。

Android的图形显示架构是怎样的呢,简单说,APP负责画图(开发UI),系统服务负责控制图层,SurfaceFlinger负责混合图层并显示。

如果用画图来比喻,那么什么是画笔呢?

  • Skia:CPU绘制2D图形(软件渲染)
  • OpenGL: GPU绘制3D图形(硬件渲染)

有了画笔,还需要在画布上画画,那什么是画布呢?

  • FramebufferNativeWindow: Android4.4版本之前,它可用于SurfaceFlinger
  • Surface: 在Android4.4版本之后,仅使用Surface

此外还需要混合图层:

  • GPU: 使用OpenGL来合成图层
  • HWC: 使用HWC来合成图层(Hardware Composer,硬件混合渲染器。首次在Android 3.0被引入。主要的目的是选择最高效的途径来合成混合图层。属于HAL层,它的实现依赖具体设备,通常由硬件厂家完成)

从应用到上屏显示,整个显示过程由三个进程一起配合完成:App,System Server和SurfaceFlinger

注意,在Android4.4之后版本,所有的图形都是绘制在Surface上,不管是应用程序画图还是SurfaceFlinger混合图层。应用程序可以通过Skia来绘制2D图形,也可以用OpenGL来绘制3D图形,SurfaceFlinger通过OpenGL来混合图形到指定的Surface上,然后这个Surface与其他没有被SurfaceFlinger合成的图层一起再次送往HWC进行合成。

SurfaceFlinger可以使用OpenGL ES合成Layer,这需要占用并消耗GPU资源。大多数GPU都没有针对图层合成进行优化,当SurfaceFlinger通过GPU合成图层时,应用程序无法使用GPU进行自己的渲染。而HWC通过硬件设备进行图层合成,可以减轻GPU的合成压力。具体流程,参见HWC文档的说明。

这里再以Activity显示的简单流程为例:

  1. startActivity启动Activity;
  2. Activity创建一个window(PhoneWindow),并在WindowManagerService中注册这个window
  3. WindowManagerService会要求SurfaceFlinger为这个window创建一个surface用来绘图。SurfaceFlinger创建一个Surface。(SurfaceFlinger中Surface中以Layer来体现,即App中的一个Surface ,对应SurfaceFlinger的一个Layer),这个Layer的核心即是一个BufferQueue,此时App页面就可以在这个Layer上渲染了
  4. 将所有的Layer进行合成,显示到屏幕上

一般而言,屏幕至少会有三个layer,屏幕顶端的status bar,屏幕下面的navigation bar(虚拟按键),App的UI部分。App的layer可能有多个。status bar和navigation bar是由系统进行渲染。而App的UI部分对应的layer 是由它自己去处理(通知SurfaceFlinger处理),最后再把这些layer合成 。

软件渲染与硬件渲染

从 Android 3.0 开始,支持硬件加速,到 4.0 时,默认开启硬件加速

硬件加速渲染与软件渲染整个流程差异较大,最核心就是通过 GPU 完成 Graphic Buffer 的内容绘制。硬件加速大大提高 Android 系统显示和刷新的速度,但是也存在一些问题:一方面加大内存消耗,OpenGL API 和Graphic Buffer 缓冲区占用内存。另外还存在耗电、兼容性等问题。

1820210-62fd602463eee1dc

UI控件在绘制到屏幕之前,需要经过栅格化操作,而栅格化非常耗时。GPU 主要用于处理图形运算,可以加快栅格化的过程。

1820210-b0b26afca431962d

图形显示开发

就目前来说,应用开发者可通过三种方式将图像显示到屏幕上:即 CanvasOpenGL ESVulkan,其中Vulkan是较新的技术,兼容不是很好,主要是前两种。这里Canvas即对应着Skia 2D引擎。

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

简单说,图形图像的显示就是一个围绕BufferQueue 的生产者——消费者模型。这一点,官方文档也做了详细说明

ape-fwk-graphics

  • BufferQueue:Producer向BufferQueue中写数据,Consumer从BufferQueue中读数据。
  • Producer:生产者。因为应用程序不断地刷新UI,从而将产生的显示数据源源不断地写到BufferQueue中。当Producer需要使用一块buffer时,调用BufferQueuedequeue函数,获得的buffer就只属于producer,当生产者认为一块buffer已经写入完成后,调用BufferQueuequeue函数,把buffer归还到BufferQueue的队列中。
  • Consumer:消费者 。当生产者准备好数据后,BufferQueue就会调用onFrameAvailable()来通知Consumer进行消费

图像生产者 :相机 或 基于OpenGL ES 游戏生成的预览。 图像消费者 : SurfaceFlinger 或显示 OpenGL ES 流的另一个应用,如显示相机取景器的相机应用。

生产者和消费者可能是在不同的进程,它们的通信不是用Binder,为了高效传输大块数据,使用匿名共享内存,BufferQueue 不会复制缓冲区内容;通过句柄进行传递。

bufferqueue

SurfaceView 与 TextureView

20210105221835161

在开发中,我们想使用Surface,可以通过两个控件SurfaceView 和 TextureView来获取。

SurfaceView 继承自View,并提供了一个可以嵌入到View结构树中的独立的绘图层,你可以完全控制这个绘图层,比如说设定它的大小。SurfaceView负责将surface放置在屏幕上的正确位置。它是Surface的持有者,通过SurfaceHolder来对Surface进行管理控制的。SurfaceView的一大特性是允许其他线程(非UI线程)绘制图形(Canvas)

public class MySurfaceView extends SurfaceView  implements Callback{  

    public MySurfaceView(Context context) {  
        super(context);  
        init();
    }  

    private void init(){  
        SurfaceHolder holder = getHolder();  
        holder.addCallback(this); //设置Surface生命周期回调  
    }  

    // surface变化的时候回调(格式/大小)
    @Override  
    public void surfaceChanged(SurfaceHolder holder, int format, int width,  
            int height) {  
    }  

    // surface创建时回调
    @Override  
    public void surfaceCreated(SurfaceHolder holder) {  
    }  

    // 隐藏并被销毁时回调
    @Override  
    public void surfaceDestroyed(SurfaceHolder holder) {  
    }  
}

TextureView是Android 4.0中引入的控件,主要是为了解决一些SurfaceView的缺陷。与SurfaceView相比,TextureView不会创建一个单独的窗口,这使得它可以像一般的View一样执行一些变换操作,比如移动、动画等等,例如,你可以通过调用setAlpha将TextureView设置成半透明。

tv = (TextureView) findViewById(R.id.texture);
// 设置监听
tv.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    //SurfaceTexture创建成功
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // 执行操作,如播放视频
    }

    //大小发生变化
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    }

    //被销毁(在此回调释放资源)
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    //更新
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    }
});

两者的对比

TextureView SurfaceView
绘制 1-3帧延迟
内存
动画和截图 支持 不支持
耗电

SurfaceView 和 TextureView的使用总结

  • SurfaceView是独立的一个Window,不存在于界面的view体系结构中,因此不能进行动画、平移、缩放,两个SurfaceView不能相互覆盖。由于不在同一层级中,也难以放在ListView或者ScrollView中。

  • TextureView更像一般的View,可以被缩放、平移,也能进行动画。但它只能在开启了硬件加速的Window中使用,并且消费的内存要比SurfaceView多

  • TextureView在5.0版本前在主线程渲染,在5.0后可在单独线程渲染

  • TextureView内部封装了SurfaceTexture,SurfaceTexture用于图像流数据的二次处理(如Camera滤镜、美颜、桌面特效等)。与SurfaceView的直接输出相比,这样就会有若干帧的延迟。实际上SurfaceView+SurfaceTexture也能完成类似的功能。

  • TextureView实现视频播放时需注意,接收视频流的对象是SurfaceTexture,而不是TextureView。TextureView只是作为一个硬件加速的展示层。所以如果需要无缝衔接的播放(比如大小屏幕切换播放),TextureView无需复用,但一定要保证SurfaceTexture的复用。

  • TextureView中获得一个Surface,使用:new Surface(textTureView.getSurfaceTexture())


公众号“编程之路从0到1”

20190301102949549

Copyright © Arcticfox 2021 all right reserved,powered by Gitbook文档修订于: 2022-05-01 12:20:20

results matching ""

    No results matching ""