Android图像系统与Choreographer

一.图像显示系统概述

一个基础的图像显示系统由CPU、GPU、屏幕三部分组成。CPU负责计算帧数据,GPU负责渲染图形数据,屏幕负责图像的显示。在屏幕显示图像时,会按照从上向下逐行扫描的方式扫描每一帧图像的像素。

由于CPU与GPU处理数据的时间不可控,因此会导致屏幕刷新率与渲染帧率不一致。具体表现为在扫描第一帧图像扫描的过程中出现第二帧图像,引起屏幕中画面的撕裂。

1.双缓存机制和VSync机制

为了解决画面的撕裂,在图像显示系统中引入了双缓存机制和VSync机制。

双缓存机制是指使用frameBuffer和backBuffer两块内存作为帧缓存,屏幕每次从frameBuffer中获取当前帧图像进行扫描,在屏幕扫描的同时,CPU与GPU使用backBuffer渲染下一帧图像。

VSync机制是指当屏幕扫描完最后一行像素时,需要返回到第一行像素的位置,这时屏幕设备会产生一个垂直同步信号,即VSync。在收到VSync信号后,frameBuffer与backBuffer中的图像数据进行交换。交换完成后,屏幕会继续从frameBuffer中扫描下一帧图像。屏幕每秒钟产生的VSync信号的次数就是帧率。

当一个VSync信号发出后,如果在下一次VSync信号到来前,GPU还没有完成下一帧数据的渲染,此时frameBuffer中的数据无法与backBuffer中的数据进行交换,会出现屏幕持续扫描同一帧图像的现象,即掉帧。

2.三缓存机制

通常情况下,掉帧是由于绘制的图像过于复杂,需要消耗大量的时间。为了解决这个问题,Android系统会在每次收到VSync信号后,尽可能去安排CPU计算帧数据,以此来减少掉帧问题的发生。

同时,Android引入了三缓存机制。三缓存机制就是在frameBuffer和backBuffer之外,又加入了graphicBuffer。 当出现一次掉帧后,会启用三缓存机制,以此减少后续掉帧问题的发生。 ![UML 图_edit_104867516109518.jpg](https://img-blog.csdnimg.cn/img_convert/1cb996ddc43cb4b896b5b2add81bcbf2.webp?x-oss-process=image/format,png)     当然,帧缓存也不是越多越好,帧缓存过多会造成内存压力,降低画面的实时性。

二.Choreographer

Choreographer是Android中用于监听VSync信号,并在VSync信号到来时执行指定任务的组件。Choreographer是线程单实例,它的创建依赖当前线程的Looper,可以通过Choreographer的静态方法getInstance获取。

1.Choreographer的初始化

在Choreographer的构造方法中,会创建三个重要的对象mHandler、mDisplayEventReceiver、mCallbackQueues,它们对应的类型分别为FrameHandler、FrameDisplayEventReceiver、Array。
  • FrameHandler:用于将操作切换到Choreographer对应的线程。
  • FrameDisplayEventReceiver:用于请求和接收VSync信号。
  • Array:用于添加待执行的任务,由于callbackQueue的类型有五种类型,因此数组长度为5。
    //输入事件,首先执行
    public static final int CALLBACK_INPUT = 0;
    //动画,第二执行
    public static final int CALLBACK_ANIMATION = 1;
    //插入更新的动画,第三执行
    public static final int CALLBACK_INSETS_ANIMATION = 2;
    //绘制,第四执行
    public static final int CALLBACK_TRAVERSAL = 3;
    //提交,最后执行,
    public static final int CALLBACK_COMMIT = 4;

2.Choreographer的使用

2.1 添加任务

当需要在VSync信号到来时执行某些操作,可以通过调用Choreographer的postCallback方法实现,该方法接收一个Runnable对象作为参数。

在Choreographer的postCallback方法中会调用postCallbackDelayed方法,最终会调用postCallbackDelayedInternal方法。

UML 图 (1)_edit_98503826421947.jpg 在Choreographer的postCallbackDelayedInternal方法内部主要做了四件事:

1)计算添加当前任务的时间戳。

2)根据任务类型,获取任务队列。

3)将任务添加到任务队列中。

4)请求VSync信号。

UML 图 (2)_edit_98613840305264.jpg 在CallbackQueue的addCallbackLocked方法中,主要做了三件事:

1)将当前任务封装成CallbackRecord。

2)从头遍历任务链表,按照添加任务的时间戳从小到大的顺序,找到当前任务对应的位置。

3)将当前任务插入到对应位置。

UML 图 (3)_edit_104747089747036.jpg 在Choreographer的scheduleFrameLocked方法中,主要做了三件事:

1)检查当前方法是否运行在Choreographer对应的线程。

2)如果运行在Choreographer对应的线程,则请求VSync信号。

3)如果没有运行在Choreographer对应的线程,则通过FrameHandler切换到对应线程请求VSync信号。

UML 图 (4)_edit_104764844536617.jpg

2.1 任务执行

当VSync信号到来时,会回调FrameDisplayEventReceiver的onVsync方法。onVsync方法内部会调用FrameHandler方法发送异步消息,将线程切换到Choreographer对应的线程。

UML 图 (5)_edit_104833015791294.jpg 最终会调用FrameDisplayEventReceiver的run方法。由于FrameDisplayEventReceiver是Choreographer的内部类,run方法内部会直接调用Choreographer的doFrame方法。在doFrame方法中,首先会根据时间戳计算并处理掉帧,然后按照CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_INSETS_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT顺序,调用doCallbacks方法。

UML 图 (6)_edit_104839577852231.jpg 在Choreographer的doCallbacks方法中,主要做了三件事:

1)根据任务类型,获取任务队列。

2)从任务队列中获取第一个任务并执行。

3)获取下一个任务,继续执行,直到任务队列中所有任务都执行完毕。

UML 图 (7)_edit_104849792851187.jpg 在CallbackRecord的run方法中,会根据添加任务时传递的token区分任务类型。

如果是通过postVsyncCallback方法添加的任务,则任务类型为VsyncCallback,会调用VsyncCallback的onVsync方法。

如果是通过postFrameCallback方法添加的任务,则任务类型为FrameCallback,会调用FrameCallback的doFrame方法。

如果是通过postCallback方法添加的任务,则任务类型为Runnable,会调用Runnable的run方法。

UML 图 (8)_edit_104859208889728.jpg
如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料

在这里插入图片描述
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值