Optymalizacja wykorzystania pamięci

Optymalizacja pamięci jest kluczowa dla zapewnienia płynnego działania, zapobiegania awariom aplikacji oraz utrzymania stabilności systemu i prawidłowego działania platformy. Chociaż w przypadku każdej aplikacji należy monitorować i optymalizować wykorzystanie pamięci, aplikacje z treściami na telewizory mają specyficzne problemy, które różnią się od typowych aplikacji na Androida na urządzenia przenośne.

Duże zużycie pamięci może powodować problemy z działaniem aplikacji i systemu, takie jak:

  • Aplikacja może działać wolno lub z opóźnieniem, a w najgorszym przypadku zostać zamknięta.
  • Usługi systemowe widoczne dla użytkownika (np. sterowanie głośnością, panel ustawień obrazu, asystent głosowy) działają bardzo wolno lub mogą w ogóle nie działać.
  • Komponenty systemu mogą zostać zatrzymane, a następnie ponownie uruchomione, co powoduje gwałtowne zwiększenie konkurencji o zasoby i bezpośrednie wpływa na aplikację na pierwszym planie.
  • Przejście do Launchera może się znacznie opóźnić, a aplikacja na pierwszym planie może wydawać się nieresponsywna, dopóki nie zakończy się przejście.
  • System może przejść do bezpośredniego odzyskiwania, czyli tymczasowo wstrzymać wykonywanie wątków podczas oczekiwania na przydzielenie pamięci. Może się to zdarzyć w przypadku dowolnego wątku, np. głównego lub wątków związanych z kodekiem, co może powodować utraty klatek dźwięku i obrazu oraz błędy interfejsu.

Zagadnienia dotyczące pamięci na urządzeniach TV

Urządzenia telewizyjne mają zwykle znacznie mniej pamięci niż telefony czy tablety. Na przykład konfiguracja, którą widzimy na telewizorze, to 1 GB pamięci RAM i rozdzielczość wideo 1080p. Jednocześnie większość aplikacji na telewizory ma podobne funkcje, a więc podobne wdrożenia i wspólne problemy. Te 2 sytuacje stanowią problemy, których nie ma w przypadku innych typów urządzeń i aplikacji:

  • Aplikacje do oglądania multimediów zwykle składają się z widoków obrazów w formie siatkitła w pełnym ekranie, które wymagają załadowania dużej liczby obrazów do pamięci w krótkim czasie.
  • Aplikacje telewizyjne odtwarzają strumienie multimedialne, które wymagają przydzielenia określonej ilości pamięci na potrzeby odtwarzania obrazu i dźwięku. Muszą też mieć znaczne bufory multimedialne, aby zapewnić płynne odtwarzanie.
  • Dodatkowe funkcje multimedialne (przewijanie, zmiana odcinka, zmiana ścieżki audio itp.) mogą wymagać dodatkowej pamięci, jeśli nie są prawidłowo zaimplementowane.

Informacje o telewizorach

Ten przewodnik koncentruje się głównie na wykorzystaniu i docelowym wykorzystaniu pamięci przez aplikacje na urządzeniach z małą ilością pamięci RAM.

W przypadku urządzeń telewizyjnych weź pod uwagę te cechy:

  • Pamięć urządzenia: ilość pamięci operacyjnej (RAM) zainstalowanej na urządzeniu.
  • Rozdzielczość interfejsu użytkownika urządzenia: rozdzielczość, której urządzenie używa do renderowania interfejsu systemu operacyjnego i aplikacji; jest ona zazwyczaj niższa niż rozdzielczość wideo urządzenia.
  • Rozdzielczość filmu: maksymalna rozdzielczość, w której urządzenie może odtwarzać filmy.

Prowadzi to do podziału różnych typów urządzeń na kategorie i określenia sposobu wykorzystywania przez nie pamięci.

Podsumowanie dotyczące urządzeń TV

Pamięć urządzenia Rozdzielczość filmu na urządzeniu Rozdzielczość interfejsu użytkownika urządzenia isLowRAMDevice()
1 GB 1080p 720p Tak
1,5 GB 2160p 1080p Tak
≥1,5 GB 1080p 720p lub 1080p Nie*
≥2 GB 2160p 1080p Nie*

Urządzenia z niewielką ilością pamięci RAM

Te urządzenia znajdują się w sytuacji ograniczonej pamięci i będą zgłaszać ActivityManager.isLowRAMDevice() wartość „prawda”. Aplikacje działające na urządzeniach z Androidem TV o małej ilości pamięci RAM muszą stosować dodatkowe mechanizmy kontroli pamięci.

Do tej kategorii zaliczamy urządzenia o tych cechach:

  • Urządzenia z 1 GB pamięci RAM: 1 GB pamięci RAM, interfejs w rozdzielczości 720p/HD (1280 x 720), filmy w rozdzielczości 1080p/FullHD (1920 x 1080).
  • Urządzenia z 1,5 GB pamięci RAM: 1,5 GB pamięci RAM, rozdzielczość interfejsu 1080p/FullHD (1920 x 1080), rozdzielczość wideo 2160p/UltraHD/4K (3840 x 2160)
  • Inne sytuacje, w których OEM zdefiniował flagę ActivityManager.isLowRAMDevice() ze względu na dodatkowe ograniczenia pamięci.

Zwykłe telewizory

Te urządzenia nie mają problemu z niedostateczną ilością pamięci. Uważamy, że te urządzenia mają te cechy:

  • 1,5 GB pamięci RAM, interfejs w rozdzielczości 720p lub 1080p oraz filmy w rozdzielczości 1080p
  • 2 GB pamięci RAM, interfejs w rozdzielczości 1080p i filmy w rozdzielczości 1080p lub 2160p

Nie oznacza to jednak, że aplikacje nie powinny zwracać uwagi na wykorzystanie pamięci na tych urządzeniach, ponieważ niektóre konkretne przypadki niewłaściwego wykorzystania pamięci mogą wyczerpywać dostępną pamięć i spowodować pogorszenie wydajności.

Docelowe wartości pamięci na urządzeniach z Androidem TV o małej ilości pamięci RAM

Podczas pomiaru pamięci na tych urządzeniach zdecydowanie zalecamy monitorowanie wszystkich sekcji pamięci za pomocą profilatora pamięci w Android Studio. Aplikacje TV powinny przeprowadzić profilowanie wykorzystania pamięci i zadbać o to, aby ich kategorie nie przekraczały progów określonych w tej sekcji.

profilowanie pamięci

W sekcji Jak jest liczona pamięć znajdziesz szczegółowe wyjaśnienie raportowanych wartości pamięci. Przy definiowaniu progów dla aplikacji na telewizory skupimy się na 3 kategoriach pamięci:

  • Anonimowa + wymiana: składa się z kompozycji Java + Natywna + pamięć alokowana w Android Studio.
  • Grafika: zgłoszone bezpośrednio w narzędziu do profilowania. Zwykle składa się z tekstur graficznych.
  • Plik: zgłoszony jako kategoria „Kod” i „Inne” w Android Studio.

W związku z tymi definicjami w tabeli poniżej podano maksymalną wartość, której powinien używać każdy typ grupy pamięci:

Typ pamięci Cel Docelowe wartości wykorzystania (1 GB)
Anonimowy + wymiana (Java + natywny + stos) Używane do alokacji, buforów multimediów, zmiennych i innych zadań wymagających dużej ilości pamięci. < 160 MB
Grafika Służy do obsługi zasobów związanych z teksturami i wyświetlaniem. 30–40 MB
Plik Używany do stron kodu i plików w pamięci. 60–80 MB

Maksymalna łączna pamięć (Anon+Swap + Graphics + File) nie może przekraczać:

  • 280 MB łącznego wykorzystania pamięci (Anon+Swap + Graphics + File) na urządzeniach z 1 GB pamięci RAM.

Zdecydowanie zalecamy, aby nie przekraczać tych wartości:

  • 200 MB pamięci (Anon+Swap + Graphics).

Pamięć pliku

Ogólne wskazówki dotyczące pamięci używanej przez pliki:

  • Ogólnie pamięć plików jest dobrze zarządzana przez system operacyjny.
  • Obecnie nie stwierdziliśmy, aby był to główny powód obciążenia pamięci.

Podczas pracy z pamięcią pliku należy jednak pamiętać o następujących kwestiach:

  • Nie uwzględniaj nieużywanych bibliotek w kompilacji i w miarę możliwości używaj małych podzbiorów bibliotek, a nie pełnych bibliotek.
  • Nie zostawiaj otwartych dużych plików w pamięci i zwalniaj je, gdy tylko skończysz z nimi pracować.
  • Zminimalizuj rozmiar skompilowanego kodu w przypadku klas Java i Kotlin. Zapoznaj się z poniższym przewodnikiem: Zmniejsz rozmiar, zaciemnij i zoptymalizuj aplikację.

konkretne rekomendacje dotyczące telewizji,

W tej sekcji znajdziesz konkretne zalecenia dotyczące optymalizacji wykorzystania pamięci na urządzeniach sterowanych przez telewizor.

Pamięć graficzna

Używaj odpowiednich formatów i rozdzielczości obrazów.

  • Nie wczytuj obrazów o wyższej rozdzielczości niż rozdzielczość interfejsu użytkownika urządzenia. Na przykład obrazy w rozdzielczości 1080p należy zmniejszyć do rozdzielczości 720p na urządzeniu z interfejsem 720p.
  • Jeśli to możliwe, używaj bitmap obsługiwanych sprzętowo.
    • W przypadku bibliotek takich jak Glide włącz funkcję Downsampler.ALLOW_HARDWARE_CONFIG, która jest domyślnie wyłączona. Włączenie tej opcji zapobiega duplikowaniu bitmap, które w przeciwnym razie byłyby zarówno w pamięci graficznej, jak i w pamięci anonimowej.
  • Unikaj pośrednich renderowań i ponownego renderowania.
    • Można je zidentyfikować za pomocą Android GPU Inspector:
    • W sekcji „Tekstury” poszukaj obrazów, które są etapami prowadzącymi do końcowego renderowania, a nie tylko elementów tworzących te obrazy. Jest to tzw. renderowanie pośrednie.
    • W przypadku aplikacji na Androida za pomocą flagi układu forceHasOverlappedRendering:false można często usunąć te elementy, aby wyłączyć pośrednie renderowanie tego układu.
    • Zapoznaj się z artykułem Avoid Overlapping Renderings (w języku angielskim) na temat nakładających się renderów, który jest świetnym źródłem informacji.
  • Unikaj wczytywania obrazów zastępczych. W miarę możliwości używaj @android:color/ lub @color do zastępczych tekstur.
  • Unikaj łączenia wielu obrazów na urządzeniu, jeśli kompozycja może zostać wykonana w trybie offline. preferuj wczytywanie samodzielnych obrazów zamiast tworzenia kompozycji z pobranych obrazów;
  • Aby lepiej pracować z pikselowymi obrazami, postępuj zgodnie z przewodnikiem dotyczącym obsługi bitmap.

Pamięć Anon+Swap

Anon+Swap składa się z alokacji natywnych, Javy i sterty w profilu pamięci w Android Studio. Użyj ActivityManager.isLowMemoryDevice(), aby sprawdzić, czy urządzenie ma ograniczoną ilość pamięci, i stosuj się do tych wskazówek.

  • Media:
    • Określ zmienny rozmiar buforów multimediów w zależności od pamięci RAM urządzenia i rozdzielczości odtwarzania wideo. Powinien on obejmować 1 minutę odtwarzania filmu:
      1. 40–60 MB na 1 GB / 1080p
      2. 60–80 MB na 1,5 GB / 1080p
      3. 80–100 MB na 1,5 GB / 2160p
      4. 100–120 MB w przypadku 2 GB / 2160p
    • Uwolnienie pamięci multimedialnej podczas zmiany odcinka, aby zapobiec zwiększaniu łącznej ilości pamięci anonimowej.
    • Natychmiast zwalniaj i zatrzymaj zasoby multimedialne, gdy aplikacja zostanie zatrzymana: do obsługi zasobów audio i wideo używaj funkcji wywołania cyklu życia. Jeśli nie jesteś aplikacją audio, zatrzymaj odtwarzanie, gdy onStop() wystąpi w Twoich działaniach, zapisz całą wykonywaną pracę i ustaw zasoby do zwolnienia. Aby zaplanować zadania, których możesz potrzebować później. Przejdź do sekcji Wątek i alarmy.
    • Podczas przewijania filmu zwracaj uwagę na pamięć buforową: deweloperzy często przeznaczają dodatkowe 15–60 sekund na przyszłe treści, aby film był gotowy do odtworzenia dla użytkownika, ale powoduje to dodatkowe obciążenie pamięci. Ogólnie nie przechodź więcej niż 5 s do przodu, dopóki użytkownik nie wybierze nowej pozycji filmu. Jeśli podczas przewijania musisz z góry buforować dodatkowy czas, pamiętaj o tym, aby:
      • Przydziel bufor przeskakiwania z wyprzedzeniem i używaj go ponownie.
      • Rozmiar bufora nie powinien przekraczać 15–25 MB (w zależności od pamięci urządzenia).
  • Przydziały:
    • Skorzystaj z wskazówek dotyczących pamięci na grafikę, aby nie powielać obrazów w pamięci anonimowej.
      • Obrazy często zajmują najwięcej pamięci, więc ich duplikowanie może znacząco obciążać urządzenie. Jest to szczególnie ważne podczas intensywnego przechodzenia przez widoki siatki obrazów.
    • Zwolnij alokacje, usuwając ich odwołania podczas przenoszenia ekranów: upewnij się, że nie ma żadnych odwołań do bitmap i obiektów.
  • Biblioteki:
    • Profile pamięci z bibliotek podczas dodawania nowych, ponieważ mogą one wczytywać dodatkowe biblioteki, które mogą również dokonywać alokacji i tworzyć wiązania.
  • Nawiązywanie kontaktów:
    • Nie wykonuj blokujących wywołań sieci podczas uruchamiania aplikacji, ponieważ wydłużają one czas uruchamiania aplikacji i wywołują dodatkowe obciążenie pamięci, która jest szczególnie ograniczona przez wczytywanie aplikacji. Najpierw wyświetlaj ekran wczytywania lub ekran powitalny, a żądania sieciowe wysyłaj dopiero po wyświetleniu interfejsu użytkownika.

Powiązania

Powiązania wymagają dodatkowej pamięci, ponieważ wprowadzają inne aplikacje do pamięci lub zwiększają zużycie pamięci przez powiązaną aplikację (jeśli jest ona już w pamięci), aby ułatwić wywołanie interfejsu API. W efekcie zmniejsza dostępną pamięć dla aplikacji na pierwszym planie. Podczas wiązania usługi należy pamiętać, kiedy i jak długo jest ona używana. Pamiętaj, aby zwolnić zablokowanie, gdy nie jest już potrzebne.

Typowe wiązania i sprawdzone metody:

  • Play Integrity API: służy do sprawdzania integralności urządzenia.
    • Sprawdzanie integralności urządzenia po ekranie wczytywania i przed odtworzeniem multimediów
    • Przed odtworzeniem treści udostępnij odwołania do PlayIntegrity StandardIntegrityManager.
  • Biblioteka płatności Google Play: służy do zarządzania subskrypcjami i zakupami w Google Play.
  • GMS FontsProvider
    • Na urządzeniach z małą ilością pamięci RAM lepiej jest używać czcionek samodzielnych niż korzystać z usług dostawcy czcionek, ponieważ pobieranie czcionek jest kosztowne, a FontsProvider będzie wiązać usługi, aby to zrobić.
  • Biblioteka Asystenta Google: czasami służy do wyszukiwania i wyszukiwania w aplikacji. Jeśli to możliwe, zastąp tę bibliotekę.
    • Aplikacje leanback: użyj funkcji Gboard Text to Speech lub biblioteki androidx.leanback.
      • Aby zaimplementować wyszukiwanie, postępuj zgodnie z wytycznymi dotyczącymi wyszukiwania.
      • Uwaga: interfejs leanback został wycofany, a aplikacje powinny przejść na interfejs TV Compose.
    • W przypadku aplikacji Compose:
      • Aby wdrożyć wyszukiwanie głosowe, użyj funkcji konwersji tekstu na mowę w Gboard.
    • Wprowadź funkcję Obejrzyj następny, aby ułatwić znajdowanie treści multimedialnych w aplikacji.

Usługi działające na pierwszym planie

Usługi na pierwszym planie to specjalny typ usługi powiązanej z powiadomieniem. To powiadomienie wyświetla się na pasku powiadomień na telefonach i tabletach, ale urządzenia telewizyjne nie mają paska powiadomień w takim samym znaczeniu jak te urządzenia. Mimo że usługi na pierwszym planie są przydatne, ponieważ mogą działać, gdy aplikacja jest w tle, aplikacje na telewizory muszą być zgodne z tymi wytycznymi:

Na Androidzie TV i Google TV usługi działające na pierwszym planie mogą działać tylko wtedy, gdy użytkownik opuści aplikację:

  • W przypadku aplikacji audio: usługi działające na pierwszym planie mogą działać tylko wtedy, gdy użytkownik opuści aplikację, aby kontynuować odtwarzanie ścieżki audio. Usługa musi zostać zatrzymana natychmiast po zakończeniu odtwarzania dźwięku.
  • W przypadku innych aplikacji: wszystkie usługi działające na pierwszym planie muszą zostać zatrzymane , gdy użytkownik opuści aplikację, ponieważ nie ma powiadomienia informującego użytkownika, że aplikacja nadal działa i korzysta z zasobów.
  • Do zadań wykonywanych w tle, takich jak aktualizowanie rekomendacji czy Warto obejrzeć, użyj WorkManager.

Zadania i alarmy

WorkManager to najnowocześniejszy interfejs API Androida do planowania cyklicznych zadań w tle. WorkManager będzie używać nowego interfejsu JobScheduler, gdy będzie on dostępny (pakiet SDK 23 lub nowszy), oraz starego AlarmManager, gdy nie będzie. Aby dowiedzieć się, jak najlepiej wykonywać zaplanowane zadania na CTV, postępuj zgodnie z tymi zaleceniami:

  • Unikaj używania interfejsów API AlarmManager w pakiecie SDK 23 i nowszych, zwłaszcza AlarmManager.set(), AlarmManager.setExact() i podobnych metod, ponieważ nie pozwalają one systemowi na określenie odpowiedniego czasu wykonywania zadań (np. gdy urządzenie jest nieaktywne).
  • Na urządzeniach z małą ilością pamięci RAM nie uruchamiaj zadań, chyba że jest to absolutnie konieczne. W razie potrzeby używaj WorkManagera WorkRequest tylko do aktualizowania rekomendacji po zakończeniu odtwarzania.
  • Zdefiniuj WorkManagera Constraints, aby system mógł wykonywać zadania w odpowiednim czasie:

Kotlin

Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()

Java

Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
  • Jeśli musisz regularnie wykonywać zadania (np. aktualizować Watch Next na podstawie aktywności związanej z oglądaniem treści w aplikacji na innym urządzeniu), ogranicz zużycie pamięci do poniżej 30 MB.

Inne ogólne wskazówki

Te wytyczne zawierają ogólne informacje o programowaniu aplikacji na Androida:

  • Zminimalizuj przydziały obiektów, zoptymalizuj ich ponowne użycie i natychmiast oddzielaj nieużywane obiekty.
    • Nie przechowuj odwołań do obiektów, zwłaszcza bitmap.
    • Unikaj używania wywołań System.gc() i bezpośrednich wywołań funkcji zwalniającej pamięć, ponieważ zakłócają one proces obsługi pamięci przez system. Na przykład na urządzeniach korzystających z zRAM-u wymuszony wywołanie funkcji gc() może tymczasowo zwiększyć wykorzystanie pamięci z powodu kompresji i dekompresji pamięci.
    • Użyj LazyList, np. w przeglądarce katalogu w sekcji Tworzenie, lub RecyclerView w nieużywanym już obecnie pakiecie narzędzi interfejsu Leanback, aby ponownie wykorzystać widoki zamiast ponownie tworzyć elementy listy.
    • Elementy odczytane od zewnętrznych dostawców treści, które prawdopodobnie się nie zmienią, będą przechowywane w pamięci podręcznej lokalnie, a interwały aktualizacji będą zdefiniowane w sposób, który zapobiegnie przydzielaniu dodatkowej pamięci zewnętrznej.
  • Sprawdź, czy nie występuje wyciek pamięci.
    • Uważaj na typowe przypadki wycieku pamięci, takie jak odwołania w nitkach anonimowych, przydzielanie zasobów buforów wideo, które nigdy nie zostaną zwolnione, i inne podobne sytuacje.
    • Aby debugować wycieki pamięci, użyj zrzutu pamięci sterty.
  • Utwórz profily bazowych, aby zminimalizować ilość kompilacji just-in-time podczas uruchamiania aplikacji „na zimno”.

Podsumowanie narzędzi