Mit der Frame-Rate-API können Apps die Android-Plattform über ihre beabsichtigte Frame-Rate informieren. Sie ist für Apps verfügbar, die auf Android 11 (API-Level 30) oder höher ausgerichtet sind. Bisher haben die meisten Geräte nur eine einzige Aktualisierungsrate unterstützt, in der Regel 60 Hz. Das ändert sich jedoch. Viele Geräte unterstützen jetzt zusätzliche Bildwiederholraten wie 90 Hz oder 120 Hz. Einige Geräte unterstützen nahtlose Aktualisierungsratenwechsel, während auf anderen kurz ein schwarzer Bildschirm angezeigt wird, der in der Regel eine Sekunde lang zu sehen ist.
Der Hauptzweck der API besteht darin, Apps in die Lage zu versetzen, alle unterstützten Aktualisierungsraten des Displays besser zu nutzen. Wenn beispielsweise eine App ein 24‑Hz-Video wiedergibt, das setFrameRate()
aufruft, kann es sein, dass das Gerät die Aktualisierungsrate des Displays von 60 Hz auf 120 Hz ändert. Diese neue Aktualisierungsrate ermöglicht eine flüssige, ruckelfreie Wiedergabe von 24‑Hz-Videos. Ein 3:2‑Pulldown, wie er für die Wiedergabe desselben Videos auf einem 60‑Hz-Display erforderlich wäre, ist nicht notwendig. Das führt zu einer besseren Nutzererfahrung.
Grundlegende Nutzung
Android bietet mehrere Möglichkeiten, auf Oberflächen zuzugreifen und sie zu steuern. Daher gibt es mehrere Versionen der setFrameRate()
API. Jede Version der API verwendet dieselben Parameter und funktioniert wie die anderen:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
Die App muss die tatsächlich unterstützten Aktualisierungsraten des Displays nicht berücksichtigen. Diese können durch Aufrufen von Display.getSupportedModes()
abgerufen werden, um setFrameRate()
sicher aufzurufen. Auch wenn das Gerät beispielsweise nur 60 Hz unterstützt, rufen Sie setFrameRate()
mit der von Ihrer App bevorzugten Bildrate auf.
Bei Geräten, die keine bessere Übereinstimmung mit der Framerate der App aufweisen, bleibt die aktuelle Aktualisierungsrate des Displays bestehen.
Wenn Sie prüfen möchten, ob ein Aufruf von setFrameRate()
zu einer Änderung der Aktualisierungsrate des Displays führt, registrieren Sie sich für Benachrichtigungen über Displayänderungen, indem Sie DisplayManager.registerDisplayListener()
oder AChoreographer_registerRefreshRateCallback()
aufrufen.
Beim Aufrufen von setFrameRate()
sollten Sie die genaue Framerate übergeben, anstatt auf eine Ganzzahl zu runden. Wenn Sie beispielsweise ein mit 29,97 Hz aufgenommenes Video rendern, geben Sie 29,97 an, anstatt auf 30 aufzurunden.
Bei Video-Apps sollte der an setFrameRate()
übergebene Kompatibilitätsparameter auf Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
gesetzt werden, um der Android-Plattform einen zusätzlichen Hinweis darauf zu geben, dass die App Pulldown verwendet, um sich an eine nicht übereinstimmende Aktualisierungsrate des Displays anzupassen (was zu Ruckeln führt).
In einigen Fällen werden keine Frames mehr gesendet, die Videooberfläche bleibt aber noch einige Zeit auf dem Bildschirm sichtbar. Das ist z. B. der Fall, wenn die Wiedergabe das Ende des Videos erreicht oder der Nutzer die Wiedergabe pausiert. In diesen Fällen rufen Sie setFrameRate()
mit dem auf 0 gesetzten Parameter für die Framerate auf, um die Framerate-Einstellung der Oberfläche auf den Standardwert zurückzusetzen. Das Zurücksetzen der Framerate-Einstellung ist nicht erforderlich, wenn die Oberfläche zerstört wird oder wenn sie ausgeblendet wird, weil der Nutzer zu einer anderen App wechselt. Setzen Sie die Framerate-Einstellung nur zurück, wenn die Oberfläche sichtbar bleibt, ohne verwendet zu werden.
Nicht nahtloser Wechsel der Framerate
Auf einigen Geräten kann es beim Wechseln der Aktualisierungsrate zu visuellen Unterbrechungen kommen, z. B. zu einem schwarzen Bildschirm für ein oder zwei Sekunden. Das tritt in der Regel bei Set-Top-Boxen, TV-Panels und ähnlichen Geräten auf. Standardmäßig wechselt das Android-Framework nicht den Modus, wenn die Surface.setFrameRate()
-API aufgerufen wird, um solche visuellen Unterbrechungen zu vermeiden.
Einige Nutzer bevorzugen eine visuelle Unterbrechung am Anfang und am Ende längerer Videos. So kann die Bildwiederholrate des Displays an die Videobildrate angepasst werden, um Artefakte durch die Bildratenkonvertierung wie 3:2-Pulldown-Ruckeln bei der Filmwiedergabe zu vermeiden.
Aus diesem Grund können nicht nahtlose Aktualisierungsratenwechsel aktiviert werden, wenn sowohl der Nutzer als auch die Apps dies zulassen:
- Nutzer: Nutzer können die Einstellung Bildrate des Inhalts anpassen aktivieren, um die Funktion zu nutzen.
- Apps: Um die Funktion zu aktivieren, können Apps
CHANGE_FRAME_RATE_ALWAYS
ansetFrameRate()
übergeben.
Wir empfehlen, für lange Videos wie Filme immer CHANGE_FRAME_RATE_ALWAYS
zu verwenden. Das liegt daran, dass der Vorteil der Anpassung der Videoframerate die Unterbrechung überwiegt, die beim Ändern der Aktualisierungsrate auftritt.
Weitere Empfehlungen
Beachten Sie die folgenden Empfehlungen für häufige Szenarien.
Mehrere Oberflächen
Die Android-Plattform ist so konzipiert, dass sie Szenarien mit mehreren Oberflächen mit unterschiedlichen Einstellungen für die Bildrate korrekt verarbeitet. Wenn Ihre App mehrere Oberflächen mit unterschiedlichen Framerates hat, rufen Sie setFrameRate()
mit der richtigen Framerate für jede Oberfläche auf. Auch wenn auf dem Gerät mehrere Apps gleichzeitig ausgeführt werden, z. B. im Splitscreen- oder Bild-im-Bild-Modus, kann jede App setFrameRate()
für ihre eigenen Oberflächen aufrufen.
Die Plattform ändert sich nicht an die Framerate der App an
Auch wenn das Gerät die von der App in einem Aufruf von setFrameRate()
angegebene Bildrate unterstützt, gibt es Fälle, in denen das Gerät die Anzeige nicht auf diese Aktualisierungsrate umstellt. Beispielsweise kann eine Oberfläche mit höherer Priorität eine andere Einstellung für die Bildrate haben oder das Gerät befindet sich im Energiesparmodus, wodurch die Aktualisierungsrate des Displays begrenzt wird, um den Akku zu schonen. Die App muss auch dann korrekt funktionieren, wenn das Gerät die Aktualisierungsrate des Displays nicht an die Framerate-Einstellung der App anpasst, auch wenn das Gerät unter normalen Umständen die Aktualisierungsrate anpasst.
Es liegt an der App, wie sie reagiert, wenn die Aktualisierungsrate des Displays nicht mit der Framerate der App übereinstimmt. Bei Videos ist die Framerate auf die des Quellvideos festgelegt. Für die Wiedergabe der Videoinhalte ist ein Pulldown erforderlich. Ein Spiel kann stattdessen versuchen, mit der Aktualisierungsrate des Displays zu laufen, anstatt bei seiner bevorzugten Framerate zu bleiben. Die App sollte den Wert, den sie an setFrameRate()
übergibt, nicht basierend auf dem Verhalten der Plattform ändern. Sie sollte auf die bevorzugte Framerate der App eingestellt bleiben, unabhängig davon, wie die App Fälle behandelt, in denen die Plattform die Anfrage der App nicht berücksichtigt. Wenn sich die Gerätebedingungen ändern, sodass zusätzliche Aktualisierungsraten für das Display verwendet werden können, hat die Plattform so die richtigen Informationen, um zur bevorzugten Framerate der App zu wechseln.
Wenn die App nicht mit der Aktualisierungsrate des Displays ausgeführt werden kann, sollte sie für jeden Frame Präsentationszeitstempel angeben. Dazu kann sie einen der folgenden Plattformmechanismen verwenden:
Durch die Verwendung dieser Zeitstempel wird verhindert, dass die Plattform einen App-Frame zu früh präsentiert, was zu unnötigen Rucklern führen würde. Die korrekte Verwendung von Zeitstempeln für die Frame-Präsentation ist etwas kompliziert. Weitere Informationen zum Vermeiden von Ruckeln bei Spielen finden Sie in unserem Leitfaden zum Frame Pacing. Erwägen Sie außerdem die Verwendung der Android Frame Pacing-Bibliothek.
In einigen Fällen wechselt die Plattform möglicherweise zu einem Vielfachen der Framerate, die in setFrameRate()
angegeben ist. Beispiel: Eine App ruft setFrameRate()
mit 60 Hz auf und das Gerät schaltet das Display auf 120 Hz um. Ein möglicher Grund dafür ist, dass eine andere App eine Oberfläche mit einer Bildrate von 24 Hz hat. In diesem Fall können sowohl die 60‑Hz- als auch die 24‑Hz-Oberfläche ohne Pulldown auf dem Display mit 120 Hz wiedergegeben werden.
Wenn das Display mit einem Vielfachen der Framerate der App läuft, sollte die App für jeden Frame Präsentationszeitstempel angeben, um unnötige Ruckler zu vermeiden. Für Spiele ist die Android Frame Pacing-Bibliothek hilfreich, um die Zeitstempel für die Frame-Präsentation richtig festzulegen.
setFrameRate() im Vergleich zu preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
ist eine weitere Möglichkeit für Apps, der Plattform ihre Framerate mitzuteilen. Einige Apps möchten nur die Aktualisierungsrate des Displays ändern, nicht aber andere Einstellungen des Anzeigemodus wie die Displayauflösung. Verwenden Sie im Allgemeinen setFrameRate()
anstelle von preferredDisplayModeId
. Die setFrameRate()
-Funktion ist einfacher zu verwenden, da die App nicht die Liste der Anzeigemodi durchsuchen muss, um einen Modus mit einer bestimmten Bildrate zu finden.
setFrameRate()
bietet der Plattform mehr Möglichkeiten, eine kompatible Framerate in Szenarien auszuwählen, in denen mehrere Oberflächen mit unterschiedlichen Frameraten ausgeführt werden. Angenommen, auf einem Pixel 4 werden zwei Apps im Splitscreen-Modus ausgeführt. In der einen App wird ein Video mit 24 Hz wiedergegeben und in der anderen wird dem Nutzer eine scrollbare Liste angezeigt. Das Pixel 4 unterstützt zwei Aktualisierungsraten für das Display: 60 Hz und 90 Hz. Bei Verwendung der preferredDisplayModeId
API wird die Videooberfläche gezwungen, entweder 60 Hz oder 90 Hz auszuwählen. Durch Aufrufen von setFrameRate()
mit 24 Hz erhält die Plattform über die Videooberfläche mehr Informationen zur Bildrate des Quellvideos. So kann die Plattform für die Aktualisierungsrate des Displays 90 Hz auswählen, was in diesem Szenario besser als 60 Hz ist.
Es gibt jedoch Szenarien, in denen preferredDisplayModeId
anstelle von setFrameRate()
verwendet werden sollte, z. B. in den folgenden Fällen:
- Wenn die App die Auflösung oder andere Einstellungen für den Anzeigemodus ändern möchte, verwenden Sie
preferredDisplayModeId
. - Die Plattform wechselt nur als Reaktion auf einen Aufruf von
setFrameRate()
den Anzeigemodus, wenn der Moduswechsel schnell und für den Nutzer wahrscheinlich nicht wahrnehmbar ist. Wenn die App die Aktualisierungsrate des Displays auch dann lieber ändern möchte, wenn ein schwerwiegender Moduswechsel erforderlich ist (z. B. auf einem Android TV-Gerät), verwenden SiepreferredDisplayModeId
. - Apps, die nicht mit der Anzeige bei einem Vielfachen der Framerate der App umgehen können, was das Festlegen von Präsentationszeitstempeln für jeden Frame erfordert, sollten
preferredDisplayModeId
verwenden.
setFrameRate() im Vergleich zu preferredRefreshRate
Mit WindowManager.LayoutParams#preferredRefreshRate
wird eine bevorzugte Framerate für das Fenster der App festgelegt. Die Rate gilt für alle Oberflächen im Fenster. Die App sollte ihre bevorzugte Framerate unabhängig von den unterstützten Aktualisierungsraten des Geräts angeben, ähnlich wie setFrameRate()
, damit der Scheduler einen besseren Hinweis auf die beabsichtigte Framerate der App erhält.
preferredRefreshRate
wird für Plattformen ignoriert, die setFrameRate()
verwenden. Verwenden Sie nach Möglichkeit immer setFrameRate()
.
preferredRefreshRate im Vergleich zu preferredDisplayModeId
Wenn Apps nur die bevorzugte Aktualisierungsrate ändern möchten, sollte preferredRefreshRate
anstelle von preferredDisplayModeId
verwendet werden.
setFrameRate() nicht zu häufig aufrufen
Obwohl der setFrameRate()
-Aufruf in Bezug auf die Leistung nicht sehr kostspielig ist, sollten Apps ihn nicht bei jedem Frame oder mehrmals pro Sekunde aufrufen.setFrameRate()
Aufrufe von setFrameRate()
führen wahrscheinlich zu einer Änderung der Aktualisierungsrate des Displays, was während des Übergangs zu einem Frame-Drop führen kann.
Sie sollten die richtige Framerate im Voraus ermitteln und setFrameRate()
einmal aufrufen.
Verwendung für Spiele oder andere Apps, die keine Videos enthalten
Video ist zwar der primäre Anwendungsfall für die setFrameRate()
API, sie kann aber auch für andere Apps verwendet werden. Ein Spiel, das nicht mit mehr als 60 Hz ausgeführt werden soll, um den Stromverbrauch zu senken und längere Spielsitzungen zu ermöglichen, kann beispielsweise Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
aufrufen. Auf diese Weise wird ein Gerät, das standardmäßig mit 90 Hz läuft, während des Spiels mit 60 Hz betrieben. So wird das Ruckeln vermieden, das sonst auftreten würde, wenn das Spiel mit 60 Hz und das Display mit 90 Hz laufen.
Verwendung von FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
ist nur für Video-Apps vorgesehen. Verwenden Sie für die nicht videobezogene Nutzung FRAME_RATE_COMPATIBILITY_DEFAULT
.
Strategie zum Ändern der Framerate auswählen
- Wir empfehlen dringend, dass Apps beim Anzeigen von Videos mit langer Laufzeit, z. B. Filmen,
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
aufrufen, wobei „fps“ die Framerate des Videos ist. - Wir raten dringend davon ab, dass Apps
setFrameRate()
mitCHANGE_FRAME_RATE_ALWAYS
aufrufen, wenn die Videowiedergabe voraussichtlich mehrere Minuten oder weniger dauert.
Beispiel für die Integration von Apps für die Videowiedergabe
Wir empfehlen die folgenden Schritte für die Integration von Aktualisierungsratenschaltern in Videowiedergabe-Apps:
- Entscheiden Sie sich für die
changeFrameRateStrategy
:- Wenn Sie ein langes Video wie einen Film abspielen, verwenden Sie
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Wenn Sie ein kurzes Video wie einen Filmtrailer abspielen, verwenden Sie
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
.
- Wenn Sie ein langes Video wie einen Film abspielen, verwenden Sie
- Wenn
changeFrameRateStrategy
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
ist, fahren Sie mit Schritt 4 fort. - Sie können erkennen, ob ein nicht nahtloser Wechsel der Aktualisierungsrate bevorsteht, indem Sie prüfen, ob beide der folgenden Bedingungen erfüllt sind:
- Ein nahtloser Moduswechsel ist von der aktuellen Aktualisierungsrate (C) zur Framerate des Videos (V) nicht möglich. Das ist der Fall, wenn C und V unterschiedlich sind und
Display.getMode().getAlternativeRefreshRates
kein Vielfaches von V enthält. - Der Nutzer hat sich für nicht nahtlose Änderungen der Aktualisierungsrate entschieden. Sie können dies erkennen, indem Sie prüfen, ob
DisplayManager.getMatchContentFrameRateUserPreference
MATCH_CONTENT_FRAMERATE_ALWAYS
zurückgibt.
- Ein nahtloser Moduswechsel ist von der aktuellen Aktualisierungsrate (C) zur Framerate des Videos (V) nicht möglich. Das ist der Fall, wenn C und V unterschiedlich sind und
- Wenn der Wechsel nahtlos erfolgen soll, gehen Sie so vor:
- Rufen Sie
setFrameRate
auf und übergeben Siefps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
undchangeFrameRateStrategy
. Dabei istfps
die Framerate des Videos. - Videowiedergabe starten
- Rufen Sie
- Wenn ein nicht nahtloser Moduswechsel bevorsteht, gehen Sie so vor:
- UX anzeigen, um den Nutzer zu benachrichtigen Wir empfehlen, dass Sie dem Nutzer die Möglichkeit geben, diese Benutzeroberfläche zu schließen und die zusätzliche Verzögerung in Schritt 5.d zu überspringen. Das liegt daran, dass die von uns empfohlene Verzögerung bei Displays mit schnelleren Schaltzeiten größer als nötig ist.
- Rufen Sie
setFrameRate
auf und übergeben Siefps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
undCHANGE_FRAME_RATE_ALWAYS
, wobeifps
die Framerate des Videos ist. - Warten Sie auf den
onDisplayChanged
-Callback. - Warte 2 Sekunden, bis der Moduswechsel abgeschlossen ist.
- Videowiedergabe starten
Der Pseudocode für die ausschließliche Unterstützung des nahtlosen Wechsels lautet wie folgt:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
Der Pseudocode für den nahtlosen und nicht nahtlosen Wechsel wie oben beschrieben lautet so:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (isSeamlessSwitch(contentFrameRate)) {
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
} else if (displayManager.getMatchContentFrameRateUserPreference()
== MATCH_CONTENT_FRAMERATE_ALWAYS) {
showRefreshRateSwitchUI();
sleep(shortDelaySoUserSeesUi);
displayManager.registerDisplayListener(…);
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ALWAYS);
transaction.apply();
waitForOnDisplayChanged();
sleep(twoSeconds);
hideRefreshRateSwitchUI();
beginPlayback();
}