Interfejs API częstotliwości odświeżania umożliwia aplikacjom informowanie platformy Android o zamierzonej częstotliwości odświeżania. Jest on dostępny w aplikacjach kierowanych na Androida 11 (poziom API 30) lub nowszego. Tradycyjnie większość urządzeń obsługiwała tylko jedną częstotliwość odświeżania ekranu, zwykle 60 Hz, ale to się zmienia. Wiele urządzeń obsługuje teraz dodatkowe częstotliwości odświeżania, takie jak 90 Hz lub 120 Hz. Niektóre urządzenia obsługują płynną zmianę częstotliwości odświeżania, a inne na krótko wyświetlają czarny ekran, zwykle przez sekundę.
Głównym celem interfejsu API jest umożliwienie aplikacjom lepszego wykorzystania wszystkich obsługiwanych częstotliwości odświeżania wyświetlacza. Na przykład aplikacja odtwarzająca film w 24 Hz, która wywołuje setFrameRate()
, może spowodować zmianę częstotliwości odświeżania wyświetlacza z 60 Hz na 120 Hz. Ta nowa częstotliwość odświeżania umożliwia płynne odtwarzanie filmów w 24 Hz bez drgań i bez konieczności stosowania techniki 3:2 pulldown, która byłaby wymagana do odtwarzania tego samego filmu na wyświetlaczu 60 Hz. Zapewnia to większą wygodę użytkownikom.
Podstawowe użycie
Android udostępnia kilka sposobów uzyskiwania dostępu do ekranów i sterowania nimi, dlatego istnieje kilka wersji interfejsu setFrameRate()
API. Każda wersja interfejsu API przyjmuje te same parametry i działa tak samo jak pozostałe:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
Aplikacja nie musi uwzględniać rzeczywistych obsługiwanych częstotliwości odświeżania wyświetlacza, które można uzyskać, wywołując Display.getSupportedModes()
, aby bezpiecznie wywołać setFrameRate()
. Na przykład nawet jeśli urządzenie obsługuje tylko 60 Hz, wywołaj funkcję setFrameRate()
z liczbą klatek na sekundę, która jest preferowana przez Twoją aplikację.
Urządzenia, które nie mają lepszego dopasowania do liczby klatek na sekundę aplikacji, pozostaną przy bieżącej częstotliwości odświeżania wyświetlacza.
Aby sprawdzić, czy połączenie z numerem setFrameRate()
powoduje zmianę częstotliwości odświeżania wyświetlacza, zarejestruj się, aby otrzymywać powiadomienia o zmianach wyświetlacza, dzwoniąc pod numer DisplayManager.registerDisplayListener()
lub AChoreographer_registerRefreshRateCallback()
.
Podczas wywoływania funkcji setFrameRate()
najlepiej jest przekazywać dokładną liczbę klatek na sekundę, a nie zaokrąglać jej do liczby całkowitej. Na przykład podczas renderowania filmu nagranego w 29,97 Hz przekaż wartość 29,97, a nie zaokrągloną do 30.
W przypadku aplikacji wideo parametr zgodności przekazywany do funkcji setFrameRate()
powinien mieć wartość Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, aby przekazać platformie Android dodatkową wskazówkę, że aplikacja będzie używać funkcji pulldown do dostosowywania się do niezgodnej częstotliwości odświeżania wyświetlacza (co spowoduje drgania).
W niektórych przypadkach powierzchnia wideo przestanie przesyłać klatki, ale pozostanie widoczna na ekranie przez pewien czas. Typowe sytuacje to zakończenie odtwarzania filmu lub wstrzymanie go przez użytkownika. W takich przypadkach wywołaj funkcję setFrameRate()
z parametrem liczby klatek ustawionym na 0, aby przywrócić domyślną wartość ustawienia liczby klatek powierzchni. Usuwanie ustawienia liczby klatek na sekundę w ten sposób nie jest konieczne podczas niszczenia powierzchni ani gdy jest ona ukryta, ponieważ użytkownik przełącza się na inną aplikację. Usuwaj ustawienie liczby klatek na sekundę tylko wtedy, gdy powierzchnia pozostaje widoczna, ale nie jest używana.
Niepłynne przełączanie liczby klatek
Na niektórych urządzeniach przełączanie częstotliwości odświeżania może powodować przerwy w wyświetlaniu, np. czarny ekran przez sekundę lub dwie. Zwykle występuje to w przypadku dekoderów, paneli telewizyjnych i podobnych urządzeń. Domyślnie platforma Android nie przełącza trybów po wywołaniu interfejsu Surface.setFrameRate()
, aby uniknąć takich przerw w wyświetlaniu.
Niektórzy użytkownicy wolą, aby na początku i na końcu dłuższych filmów pojawiały się przerywniki wizualne. Dzięki temu częstotliwość odświeżania wyświetlacza jest dopasowana do liczby klatek na sekundę w filmie, co pozwala uniknąć artefaktów związanych z konwersją liczby klatek na sekundę, takich jak drgania spowodowane konwersją 3:2 podczas odtwarzania filmów.
Z tego powodu przełączanie częstotliwości odświeżania bez efektu migotania można włączyć, jeśli użytkownik i aplikacje wyrażą na to zgodę:
- Użytkownicy: aby wyrazić zgodę, użytkownicy mogą włączyć ustawienie Dopasuj liczbę klatek do treści.
- Aplikacje: aby wziąć udział w programie, aplikacje mogą przekazywać wartość
CHANGE_FRAME_RATE_ALWAYS
do funkcjisetFrameRate()
.
W przypadku długich filmów, np. filmów fabularnych, zalecamy używanie CHANGE_FRAME_RATE_ALWAYS
. Dzieje się tak, ponieważ korzyści z dopasowania liczby klatek filmu przeważają nad przerwą, która występuje podczas zmiany częstotliwości odświeżania.
Dodatkowe zalecenia
Postępuj zgodnie z tymi zaleceniami w typowych scenariuszach.
Różne powierzchnie
Platforma Android została zaprojektowana tak, aby prawidłowo obsługiwać scenariusze, w których występuje wiele powierzchni z różnymi ustawieniami liczby klatek na sekundę. Jeśli aplikacja ma wiele powierzchni o różnych częstotliwościach odświeżania, wywołuj funkcję setFrameRate()
z odpowiednią częstotliwością odświeżania dla każdej powierzchni. Nawet jeśli na urządzeniu działa jednocześnie kilka aplikacji w trybie podzielonego ekranu lub obrazu w obrazie, każda z nich może bezpiecznie wywoływać funkcję setFrameRate()
na własnych powierzchniach.
Platforma nie zmienia się na liczbę klatek aplikacji
Nawet jeśli urządzenie obsługuje liczbę klatek na sekundę określoną przez aplikację w wywołaniu funkcji
setFrameRate()
, w niektórych przypadkach nie przełączy wyświetlacza na tę częstotliwość odświeżania. Na przykład powierzchnia o wyższym priorytecie może mieć inne ustawienie liczby klatek na sekundę lub urządzenie może być w trybie oszczędzania baterii (ustawiając ograniczenie częstotliwości odświeżania wyświetlacza w celu oszczędzania baterii). Aplikacja musi działać prawidłowo, nawet jeśli urządzenie nie przełącza częstotliwości odświeżania ekranu na ustawienie liczby klatek aplikacji, nawet jeśli w normalnych okolicznościach urządzenie to robi.
Aplikacja decyduje, jak reagować, gdy częstotliwość odświeżania wyświetlacza nie odpowiada liczbie klatek na sekundę w aplikacji. W przypadku wideo liczba klatek jest stała i odpowiada liczbie klatek filmu źródłowego. Aby wyświetlić treści wideo, konieczne będzie zastosowanie techniki pulldown. Gra może spróbować działać z częstotliwością odświeżania wyświetlacza zamiast utrzymywać preferowaną liczbę klatek na sekundę. Aplikacja nie powinna zmieniać wartości przekazywanej do setFrameRate()
w zależności od działania platformy. Powinna ona pozostać ustawiona na preferowaną liczbę klatek na sekundę aplikacji, niezależnie od tego, jak aplikacja obsługuje przypadki, w których platforma nie dostosowuje się do jej żądania. Dzięki temu, jeśli warunki urządzenia zmienią się tak, że będzie można używać dodatkowych częstotliwości odświeżania wyświetlacza, platforma będzie miała prawidłowe informacje, aby przełączyć się na preferowaną przez aplikację liczbę klatek na sekundę.
W przypadku, gdy aplikacja nie może działać z częstotliwością odświeżania wyświetlacza, powinna określać sygnatury czasowe prezentacji dla każdej klatki, korzystając z jednego z mechanizmów platformy do ustawiania sygnatur czasowych prezentacji:
Użycie tych sygnatur czasowych zapobiega zbyt wczesnemu wyświetlaniu przez platformę klatki aplikacji, co mogłoby powodować niepotrzebne drgania. Prawidłowe użycie sygnatur czasowych klatek prezentacji jest nieco skomplikowane. Więcej informacji o unikaniu drgań znajdziesz w naszym przewodniku po synchronizacji klatek. Możesz też skorzystać z biblioteki Android Frame Pacing.
W niektórych przypadkach platforma może przełączyć się na wielokrotność liczby klatek na sekundę określonej przez aplikację w setFrameRate()
. Aplikacja może na przykład wywołać setFrameRate()
z częstotliwością 60 Hz, a urządzenie może przełączyć wyświetlacz na 120 Hz. Może się tak zdarzyć, jeśli inna aplikacja ma powierzchnię z ustawieniem liczby klatek na sekundę wynoszącym 24 Hz. W takim przypadku wyświetlanie obrazu z częstotliwością 120 Hz umożliwi wyświetlanie obrazu z częstotliwością 60 Hz i 24 Hz bez konieczności stosowania pulldownu.
Jeśli wyświetlacz działa z częstotliwością klatek będącą wielokrotnością częstotliwości klatek aplikacji, aplikacja powinna określać sygnatury czasowe prezentacji dla każdej klatki, aby uniknąć niepotrzebnego drgania. W przypadku gier biblioteka Android Frame Pacing pomaga prawidłowo ustawiać sygnatury czasowe prezentacji klatek.
setFrameRate() a preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
to kolejny sposób, w jaki aplikacje mogą informować platformę o swojej liczbie klatek na sekundę. Niektóre aplikacje chcą tylko zmienić częstotliwość odświeżania wyświetlacza, a nie inne ustawienia trybu wyświetlania, takie jak rozdzielczość. Zamiast preferredDisplayModeId
używaj setFrameRate()
. Funkcja setFrameRate()
jest łatwiejsza w użyciu, ponieważ aplikacja nie musi przeszukiwać listy trybów wyświetlania, aby znaleźć tryb o określonej liczbie klatek na sekundę.
setFrameRate()
daje platformie więcej możliwości wyboru zgodnej liczby klatek na sekundę w sytuacjach, w których występuje wiele platform działających z różną liczbą klatek na sekundę. Rozważmy na przykład sytuację, w której na Pixelu 4 w trybie podzielonego ekranu działają 2 aplikacje: jedna odtwarza film w 24 Hz, a druga wyświetla użytkownikowi listę z możliwością przewijania. Pixel 4 obsługuje 2 częstotliwości odświeżania wyświetlacza: 60 Hz i 90 Hz. Za pomocą preferredDisplayModeId
interfejsu API można wymusić wybór częstotliwości 60 Hz lub 90 Hz. Wywołanie funkcji
setFrameRate()
z częstotliwością 24 Hz sprawia, że powierzchnia wideo przekazuje platformie więcej
informacji o częstotliwości klatek filmu źródłowego. Dzięki temu platforma może
wybrać częstotliwość odświeżania wyświetlacza 90 Hz, która w tym scenariuszu jest lepsza niż 60 Hz.
Są jednak sytuacje, w których zamiast setFrameRate()
należy użyć preferredDisplayModeId
, np.:
- Jeśli aplikacja chce zmienić rozdzielczość lub inne ustawienia trybu wyświetlania, użyj
preferredDisplayModeId
. - Platforma będzie przełączać tryby wyświetlania tylko w odpowiedzi na wywołanie funkcji
setFrameRate()
, jeśli przełączanie trybu jest proste i prawdopodobnie niezauważalne dla użytkownika. Jeśli aplikacja woli przełączyć częstotliwość odświeżania wyświetlacza, nawet jeśli wymaga to przełączenia trybu (np. na urządzeniu z Androidem TV), użyjpreferredDisplayModeId
. - Aplikacje, które nie obsługują wyświetlania z częstotliwością będącą wielokrotnością liczby klatek na sekundę, co wymaga ustawienia sygnatur czasowych prezentacji dla każdej klatki, powinny używać
preferredDisplayModeId
.
setFrameRate() a preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
ustawia preferowaną liczbę klatek na sekundę w oknie aplikacji, która obowiązuje
na wszystkich powierzchniach w tym oknie. Aplikacja powinna określać preferowaną liczbę klatek na sekundę niezależnie od obsługiwanych częstotliwości odświeżania urządzenia, podobnie jak w przypadku setFrameRate()
, aby dać harmonogramowi lepsze wskazówki dotyczące zamierzonej liczby klatek na sekundę.
W przypadku platform korzystających z setFrameRate()
parametr preferredRefreshRate
jest ignorowany. W miarę możliwości używaj setFrameRate()
.
preferredRefreshRate vs preferredDisplayModeId
Jeśli aplikacje chcą tylko zmienić preferowaną częstotliwość odświeżania, zalecamy użycie preferredRefreshRate
zamiast preferredDisplayModeId
.
Unikanie zbyt częstego wywoływania funkcji setFrameRate()
Chociaż wywołanie setFrameRate()
nie jest zbyt kosztowne pod względem wydajności, aplikacje powinny unikać wywoływania setFrameRate()
w każdej klatce lub wiele razy na sekundę. Wywołanie funkcji setFrameRate()
prawdopodobnie spowoduje zmianę częstotliwości odświeżania wyświetlacza, co może skutkować utratą klatki podczas przejścia.
Prawidłową liczbę klatek na sekundę należy określić z wyprzedzeniem i wywołać funkcję
setFrameRate()
tylko raz.
Użycie w przypadku gier lub innych aplikacji niebędących aplikacjami wideo
Chociaż interfejs setFrameRate()
API jest przeznaczony głównie do filmów, można go używać w innych aplikacjach. Na przykład gra, która nie ma działać z częstotliwością wyższą niż 60 Hz (aby zmniejszyć zużycie energii i wydłużyć sesje rozgrywki), może wywołać funkcję Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
. W ten sposób urządzenie, które domyślnie działa w 90 Hz, będzie działać w 60 Hz, gdy gra jest aktywna. Pozwoli to uniknąć drgań, które wystąpiłyby, gdyby gra działała w 60 Hz, a wyświetlacz w 90 Hz.
Korzystanie z FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
jest przeznaczony tylko dla aplikacji wideo. W przypadku wykorzystania niezwiązanego z wideo użyj FRAME_RATE_COMPATIBILITY_DEFAULT
.
Wybór strategii zmiany liczby klatek
- Zdecydowanie zalecamy, aby aplikacje wyświetlające długie filmy, takie jak filmy kinowe, wywoływały funkcję
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
, gdzie fps to liczba klatek na sekundę filmu. - Zdecydowanie odradzamy wywoływanie funkcji
setFrameRate()
z parametremCHANGE_FRAME_RATE_ALWAYS
w sytuacjach, gdy odtwarzanie filmu ma trwać kilka minut lub krócej.
Przykładowa integracja w przypadku aplikacji do odtwarzania filmów
Aby zintegrować przełączanie częstotliwości odświeżania w aplikacjach do odtwarzania filmów, wykonaj te czynności:
- Określ
changeFrameRateStrategy
:- Jeśli odtwarzasz długi film, np. pełnometrażowy, użyj
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Jeśli odtwarzasz krótki film, np. zwiastun filmu, użyj tagu
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
.
- Jeśli odtwarzasz długi film, np. pełnometrażowy, użyj
- Jeśli
changeFrameRateStrategy
ma wartośćCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
, przejdź do kroku 4. - Wykrywaj, czy ma nastąpić przełączenie częstotliwości odświeżania bez płynnego przejścia, sprawdzając, czy oba te warunki są spełnione:
- Płynne przełączanie trybów nie jest możliwe z bieżącej częstotliwości odświeżania (nazwijmy ją C) na liczbę klatek na sekundę filmu (nazwijmy ją V). Tak będzie, jeśli C i V są różne, a
Display.getMode().getAlternativeRefreshRates
nie zawiera wielokrotności V. - Użytkownik wyraził zgodę na zmiany częstotliwości odświeżania bez płynnego przejścia. Możesz to sprawdzić, sprawdzając, czy
DisplayManager.getMatchContentFrameRateUserPreference
zwracaMATCH_CONTENT_FRAMERATE_ALWAYS
.
- Płynne przełączanie trybów nie jest możliwe z bieżącej częstotliwości odświeżania (nazwijmy ją C) na liczbę klatek na sekundę filmu (nazwijmy ją V). Tak będzie, jeśli C i V są różne, a
- Jeśli przejście ma być płynne, wykonaj te czynności:
- Wywołaj funkcję
setFrameRate
i przekaż jej wartościfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
ichangeFrameRateStrategy
, gdziefps
to liczba klatek na sekundę filmu. - Rozpocznij odtwarzanie filmu
- Wywołaj funkcję
- Jeśli ma nastąpić zmiana trybu bez płynnego przejścia, wykonaj te czynności:
- Wyświetl UX, aby powiadomić użytkownika. Pamiętaj, że zalecamy wdrożenie sposobu, w jaki użytkownik może zamknąć ten interfejs i pominąć dodatkowe opóźnienie w kroku 5.d. Dzieje się tak, ponieważ zalecane przez nas opóźnienie jest większe niż to konieczne w przypadku wyświetlaczy, które charakteryzują się krótszym czasem przełączania.
- Wywołaj funkcję
setFrameRate
i przekaż jej wartościfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
iCHANGE_FRAME_RATE_ALWAYS
, gdziefps
to liczba klatek na sekundę filmu. - Poczekaj na oddzwonienie
onDisplayChanged
. - Poczekaj 2 sekundy, aż przełączanie trybu się zakończy.
- Rozpocznij odtwarzanie filmu
Pseudokod, który tylko obsługuje płynne przełączanie, wygląda tak:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
Pseudokod obsługujący płynne i niepłynne przełączanie opisane powyżej wygląda tak:
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();
}