Android Choreographer

1.在说 Choreographer 前先了解一下 CPU、帧率、垂直同步等概念:

1.1 CPU、GPU

在 Android 的绘制架构中,CPU 主要负责视图的测量、布局、记录、并且把内容计算成 Polygons(多边形) 或者 Texture(纹理) ,GPU主要复制把多边形和纹理进行 Rasterization(栅格化) 。如此才能在屏幕上成像。有时使用硬件加速后,GPU 会分担 CPU 的计算任务,而 CPU 会专注处理逻辑,这样减轻 CPU 的负担,使得整个系统效率更高。

1.2 RefreshRate(刷新率)和FrameRate(帧率)

刷新率是屏幕每秒刷新的次数。在Android平台上,这个值一般为 60HZ,即屏幕每秒刷新 60 次。

帧率是每秒绘制的帧数;通常只要帧数和刷新率保持一致,画面便流畅。所以我们应该尽量维持 60FPS 的帧率。但有时候由于视图的复杂,它们可能就会出现不一致的情况。

当帧率小于刷新率时,会出现相邻的两帧出现同一画面,便会卡顿。所以一帧画面应该尽量在 16ms 内完成(一秒为一千毫秒,因为屏幕一秒刷新六十次,所以一千除以六十约为十六毫秒),才不会出现卡顿。

那么怎样才能使刷新率和帧率保持一致?用 Vsync 可以实现。

1.3 Vsync(垂直同步)

在游戏设置中有时会出现 Vsync,开启可以使游戏更流畅。在 Android 中使用它可以使刷新率和帧率强制保持一致。

Vsync 有两个接收者:SurfaceFlinger(负责合成各个Surface)和 Choreographer (负责控制视图的绘制)。

1.4 HWComposer

它是一个c++编写的类,在 Android 系统初始化被创建,配合硬件产生 Vsync 信号。HWComposer 被设计为能够唤醒和睡眠,

这样可以使在需要时才会产生 Vsync 信号,不需要时进入睡眠状态(如:屏幕不动进入睡眠状态,屏幕每次刷新都是显示缓冲区里没发生变化的内容)。

1.5 Canvas

它实际代表了一块内存,用于储存绘制出来的数据,即 View 视图的内容都是被绘制到这个 Canvas 中。

了解完上面的内容后,就明白了,画面卡顿的原因:刷新率与帧率不一致。下面说明 Choreographer 。

2.Vsync 接收者之一 Choreographer (作用:绘制视图)

Choreographer 翻译来为舞蹈指挥,名字真骚气。。。下面看看它是做什么的。
Choreographer 是一个线程中仅存在一个实例,因此在UI线程只有一个 Choreographer 存在(一个应用中的单例)。其作用为:负责控制视图的绘制。

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            ......//省略
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
  }

上面的代码可以看到 Choreographer 调用了 postCallback() (请求 一个 Vsync 信号)方法 。先说一下 scheduleTraversals() 这个方法是怎么来的,它是 ViewRootImpl 里面的方法中出现的(点这里看scheduleTraversals()方法以及ViewTootImpl)。继续往下看 postCallback() 方法:

public void postCallback(int callbackType, Runnable action, Object token) {
    postCallbackDelayed(callbackType, action, token, 0);
}

上面代码可以看到:在里面又调用了 postCallbackDelayed() 方法,继续看这个方法:

public void postCallbackDelayed(int callbackType,
        Runnable action, Object token, long delayMillis) {
    if (action == null) {
        throw new IllegalArgumentException("action must not be null");
    }
    if (callbackType < 0 || callbackType > CALLBACK_LAST) {
        throw new IllegalArgumentException("callbackType is invalid");
    }
    postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}

上面代码可以看到:又调用了 postCallbackDelayedInternal() 方法。。。继续看这个方法:

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {

    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

上面代码可以看到:它把任务都放在 mCallbackQueues[callbackType] 队列中,接着做了一个判断:如果 dueTime <= now (意思是时间到了,便去请求一个 Vsync 信号,上面说了 16ms 会绘制一帧)调用 scheduleFrameLocked() 方法,否则时间还没到向 FrameHandler 中发送一个 MSG_DO_SCHEDULE_CALLBACK 消息(记住 FrameHandler 下面会了解下这个)。继续看 scheduleFrameLocked() 这个方法:

private void scheduleFrameLocked(long now) {
    if (USE_VSYNC) {
         if (isRunningOnLooperThreadLocked()) {
             scheduleVsyncLocked();
         } else {
             Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
             msg.setAsynchronous(true);
             mHandler.sendMessageAtFrontOfQueue(msg);
         }
    } else {
         final long nextFrameTime = Math.max(
               mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
         Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
         msg.setAsynchronous(true);
         mHandler.sendMessageAtTime(msg, nextFrameTime);
    }
}

上面代码可以看到:首先判断了 USE_VSYNC 是否为 true ,USE_VSYNC 为 true 的意思是开启垂直同步;再判断当前是否在 UI 线程,是的话请求一个 Vsync 信号,否则向 FrameHandler 中发送一个 MSG_DO_SCHEDULE_VSYNC 消息(这个消息意思就是请求一个 Vsync 信号, FrameHandler 下面会说,记住它)。可以看出,不管是不是在 UI 线程,最终都将会请求一个 Vsync 信号;往下看 scheduleVsyncLocked() 方法中有什么:

private void scheduleVsyncLocked() {
    mDisplayEventReceiver.scheduleVsync();
}

上面代码可以看到:有一个新的变量 mDisplayEventReceiver ,它调用了方法 scheduleVsync();从字面 DisplayEventReceiver 意思是显示事件接收器,那它应该是一个接收器。。。实际上它是一个 FrameDisplayEventReceiver 类型的对象,下面来看一下 FrameDisplayEventReceiver :

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
    implements Runnable {
    
    public void onVsync(){
        ...
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
}

上面代码可以看到: FrameDisplayEventReceiver 里面有一个 onVsync() 方法,这个方法用于接收 Vsync 信号,(向 FrameHandler 中发送一个消息,FrameHandler  下面会说,记住它);下面的 message 其实就是通知 Choreographer 回调 mCallbackQueues[callbackType] 中的 callback;至此,开始绘制第一帧了。

从总体上看,从一开始调用了 postCallback() 方法请求一个 Vsync 信号,到最后调用了 onVsync() 方法接收到 Vsync 信号并且通知 Choreographer 回调;这个过程就是在不断的请求 Vsync 信号,接收 Vsync 信号。

是时候看看 FrameHandler (上面有提到)是个什么了:

private final class FrameHandler extends Handler {
    public FrameHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_DO_FRAME:
                doFrame(System.nanoTime(), 0);
                break;
            case MSG_DO_SCHEDULE_VSYNC:
                doScheduleVsync();
                break;
            case MSG_DO_SCHEDULE_CALLBACK:
                doScheduleCallback(msg.arg1);
                break;
            }
        }
    }

FrameHandler主要在UI线程处理3种类型的消息:

MSG_DO_FRAME:开始渲染下一帧的操作

MSG_DO_SCHEDULE_VSYNC:请求Vsync信号

MSG_DO_SCHEDULE_CALLBACK:请求执行callback

FrameHandler 虽然不复杂,但在UI的绘制过程中具有重要的作用,所以也要了解。

知识:从上面了解到硬件每 16ms 绘制一帧,即每 16ms 产生一个 Vsync 信号;开始绘制之后,由 Choreographer 不断的去请求一个 Vsync 信号,接收一个 Vsync 信号并且回调 mCallbackQueues[callbackType] 中的 callback,这样如此的反复,布局一帧一帧的就被绘制出来了。

3.Vsync 接收者之二 SurfaceFlinger (作用:合成各个Surface)

SurfaceFlinger 是系统的一个服务,专门负责每个 Surface 中内容的合成缓存,以待显示在屏幕上。

Surface 的作用就是管理用于绘制视图树的 Canvas(上面有介绍它) 的,而我们 Window 中的视图树都是被绘制到一个由 Surface 提供的 Canvas 上。(不多说,重点在 Choreographer ,这个知道就好)。

 

更多内容戳我&

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值