Как синхронизировать частоту кадров с Android

Я ищу способ синхронизировать некоторую активность кода с частотой кадров дисплея Android. Это небольшой фрагмент кода, который просто устанавливает цвет небольшой области отображения, и ничего более. Есть какой-то метод или ловушка, которые я могу использовать для этой цели?

Я могу использовать таймер, работающий с очень быстрыми интервалами, но рендеринг, похоже, страдает от наложения частоты кадров. Например, моя частота кадров составляет 60 Гц, поэтому, если я установлю интервал таймера на 1/60 секунды, внешний вид будет неоднородным. Я не могу установить таймер точно на 16,666 мсек, и даже если бы смог, я все равно видел бы псевдонимы.

Благодарю.

1 ответ

Я знаю, что это старый вопрос, но я сам столкнулся с этой проблемой и создал симпатичный небольшой служебный класс, который делает именно это.

Котлин Класс:


/**
 * Small utility class that allows for [doFrame] to be executed every frame based on the native
 * [Choreographer] instead of using precomputed values and timers.
 * [doFrame] is run on the main thread and is executed on the next frame.
 * The [SyncedRenderer] can be restarted freely.
 */
class SyncedRenderer(val doFrame: (frameTimeNanos: Long) -> Unit) {

    private var callback: (Long) -> Unit = {}

    fun start() {
        callback = {
            doFrame(it)
            Choreographer.getInstance().postFrameCallback(callback)
        }
        Choreographer.getInstance().postFrameCallback(callback)
    }

    fun stop() {
        Choreographer.getInstance().removeFrameCallback(callback)
        callback = {}
    }
}

Применение:


val renderer = SyncedRenderer { frameTimeNanos: Long ->
    // My code here will execute on main thread every frame
}

// Example when dragging something

override fun onStartDrag() {
    renderer.start()
}

override fun onReleaseDrag() {
    renderer.stop()
}

Я обнаружил, что это ненамного эффективнее, чем просто использование фиксированной ставки. Timer и запускать код с частотой обновления каждого кадра.

Однако с этой реализацией вы не запускаете какой-либо код в фоновом потоке, поэтому вам не нужно вызывать какие-либо post или связанные с ним методы при изменении Viewс.

Что еще более важно, дополнительное выполнение не выполняется, чем необходимо, поэтому это может быть очень полезно для более плавной прокрутки и анимации по сравнению с использованием жестко запрограммированного таймера частоты обновления.

Надеюсь, это кому-то поможет:)

После некоторых поисков и экспериментов, одним из решений является использование класса Choreographer, который предоставляет способ привязать функцию обратного вызова к частоте кадров. Ниже приведен код. Я обнаружил, что это показывает небрежное время на медленных телефонах. Последние модели телефонов Samsung работают лучше.

private FrameCallback frameCallback = null;
private boolean frameCallbackPending = false;

public void armVSyncHandler() {
    if(!frameCallbackPending) {
        frameCallbackPending = true;
        if(frameCallback == null)
        {
            frameCallback = new FrameCallback() {
                @Override
                public void doFrame(long frameTimeNanos) {
                    frameCallbackPending = false;

                    // Do some work here

                    armVSyncHandler();
                }
            };
        }
        Choreographer.getInstance().postFrameCallback(frameCallback);
    }
}
Другие вопросы по тегам