如何确定移动设备上的设备屏幕刷新开始/停止

时间:2019-03-06 17:24:01

标签: android ios

在60Hz的设备上,屏幕每〜16.67ms刷新一次(我已经将其称为“刷新率和翻转计数”)。对于某些研究,这实际上是相当长的时间。屏幕刷新或开始刷新时,有什么方法可以获取准确的时间戳或事件?

1 个答案:

答案 0 :(得分:2)

在iOS上,您可以使用CADisplayLinkhttps://developer.apple.com/documentation/quartzcore/cadisplaylink

  

一个计时器对象,允许您的应用程序将其图形与显示的刷新率同步。

示例:

import Foundation
import UIKit

public class DisplayFrameSync {
    private var displayLink: CADisplayLink? = nil
    private var frameUpdate: (() -> Void)? = nil

    deinit {
        self.stop()
    }

    @objc
    private func onTick(displayLink: CADisplayLink) {
        if let onFrameUpdate = self.frameUpdate {
            onFrameUpdate()
        }
    }

    public func start(_ update: () -> Void) {
        self.displayLink?.remove(from: .main, forMode: .default)
        self.displayLink?.remove(from: .main, forMode: .tracking)
        self.displayLink?.invalidate()
        self.displayLink = nil

        self.frameUpdate = update

        self.displayLink = CADisplayLink(target: self, selector: #selector(onTick(displayLink:)))
        self.displayLink?.preferredFramesPerSecond = 60
        self.displayLink?.add(to: .main, forMode: .default)
        self.displayLink?.add(to: .main, forMode: .tracking)
    }

    public func stop() {
        self.displayLink?.remove(from: .main, forMode: .default)
        self.displayLink?.remove(from: .main, forMode: .tracking)
        self.displayLink?.invalidate()
        self.displayLink = nil
        self.frameUpdate = nil
    }
}

用法:

let ds = DisplayFrameSync()
ds.start({
    print("Frame Update")
});

DispatchQueue.main.asyncAfter(.now() + 2.0) {
    ds.stop()
}

使用上述方法,您还可以制作自己的animateWithDuration并使用Date.timeIntervalSinceReferenceDate跟踪时间,并从当前时间减去每一帧。


对于Android,您可以使用Choreographer

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Choreographer;

private class DisplayFrameSync extends Thread implements Choreographer.FrameCallback {
    private volatile Handler signal;
    private Consumer<Long> update;

    public DisplayFrameSync(Consumer<Long> update) {
        super();

        this.update = update;
    }

    @Override
    public void run() {
        setName("DisplayFrameSyncThread");

        Looper.prepare();

        signal = new Handler() {
            public void handleMessage(Message msg) {
                Looper.myLooper().quit();
            }
        };

        Choreographer.getInstance().postFrameCallback(this);

        Looper.loop();
        Choreographer.getInstance().removeFrameCallback(this);
    }

    public void signal() {
        if (this.signal != null) {
            this.signal.sendEmptyMessage(0);
            this.signal = null;
        }
    }

    @Override
    public void doFrame(long timeNano) {
        if (this.update != null) {
            this.update.accept(timeNano);
        }

        Choreographer.getInstance().postFrameCallback(this);
    }
}

这已经包含了为您渲染每个帧的时间。

用法:

DisplayFrameSync ds = new DisplayFrameSync((timeNano) -> {
    System.out.println("Frame Update");
});
ds.start();

//Later..
ds.stop();