L'ottimizzazione della memoria è fondamentale per garantire prestazioni fluide, evitare arresti anomali delle app e mantenere la stabilità del sistema e l'integrità della piattaforma. Sebbene l'utilizzo della memoria debba essere monitorato e ottimizzato in ogni app, le app di contenuti per TV presentano sfide specifiche diverse dalle tipiche app per Android per dispositivi portatili.
Un consumo elevato di memoria può causare problemi con il comportamento di app e sistema, tra cui:
- L'app stessa può diventare lenta o presentare ritardi oppure, nel peggiore dei casi, essere chiusa.
- I servizi di sistema visibili all'utente (controllo del volume, dashboard delle impostazioni dell'immagine, assistente vocale e così via) diventano molto lenti o potrebbero non funzionare affatto.
- Il processo del daemon LMK (low memory killer) potrebbe reagire a un'elevata pressione sulla memoria interrompendo i processi meno essenziali; poi questi componenti potrebbero riavviarsi poco dopo, causando picchi di ulteriore contesa delle risorse che possono influire direttamente sull'app in primo piano.
- La transizione all'Avvio app può essere notevolmente ritardata e lasciare l'app in primo piano che sembra non rispondere fino al termine della transizione.
- Il sistema potrebbe iniziare a utilizzare il recupero diretto, mettendo temporaneamente in pausa l'esecuzione di un thread in attesa dell'allocazione della memoria. Questo può accadere a qualsiasi thread, ad esempio il thread principale o i thread correlati ai codec, causando potenzialmente cali di frame audio e video e problemi dell'interfaccia utente.
Considerazioni sulla memoria sui dispositivi TV
I dispositivi TV in genere hanno molta meno memoria rispetto a smartphone o tablet. Ad esempio, una configurazione che possiamo vedere sulla TV è 1 GB di RAM e risoluzione video 1080p. Allo stesso tempo, la maggior parte delle app TV ha funzionalità simili, quindi implementazioni simili e sfide comuni. Queste due situazioni presentano problemi non riscontrati in altri tipi di dispositivi e app:
- Le app TV multimediali sono in genere composte da visualizzazioni di immagini a griglia e da immagini di sfondo a schermo intero che richiedono il caricamento di molte immagini nella memoria in un breve periodo di tempo
- Le app TV riproducono flussi multimediali che richiedono l'allocazione di una certa quantità di memoria per riprodurre video e audio e necessitano di buffer multimediali considerevoli per garantire una riproduzione fluida.
- Funzionalità multimediali aggiuntive (ricerca, cambio di episodio, cambio di traccia audio, ecc.) possono richiedere ulteriore memoria se non implementate correttamente.
Informazioni sui dispositivi TV
Questa guida si concentra principalmente sulla memoria utilizzata dalle app e sugli obiettivi di memoria per i dispositivi con poca RAM.
Sui dispositivi TV, considera queste caratteristiche:
- Memoria del dispositivo: la quantità di memoria ad accesso casuale (RAM) installata sul dispositivo.
- Risoluzione dell'interfaccia utente del dispositivo: la risoluzione utilizzata dal dispositivo per il rendering dell'interfaccia utente del sistema operativo e delle applicazioni. In genere è inferiore alla risoluzione video del dispositivo.
- Risoluzione video: la risoluzione massima a cui il dispositivo può riprodurre i video.
Ciò porta alla classificazione di diversi tipi di dispositivi e alla modalità di utilizzo della memoria.
Riepilogo dispositivi TV
| Memoria del dispositivo | Risoluzione video del dispositivo | Risoluzione dell'interfaccia utente del dispositivo | isLowRAMDevice() |
|---|---|---|---|
| 1 GB | 1080p | 720p | Sì |
| 1,5 GB | 2160p | 1080p | Sì |
| ≥ 1,5 GB | 1080p | 720p o 1080p | No* |
| ≥2 GB | 2160p | 1080p | No* |
Dispositivi TV con poca RAM
Questi dispositivi si trovano in una situazione di memoria limitata e segnaleranno
ActivityManager.isLowRAMDevice()
come true. Le applicazioni in esecuzione su dispositivi TV con poca RAM devono implementare
misure di controllo della memoria aggiuntive.
Consideriamo che i dispositivi con le seguenti caratteristiche rientrino in questa categoria:
- Dispositivi da 1 GB: 1 GB di RAM, risoluzione UI 720p/HD (1280x720), risoluzione video 1080p/FullHD (1920x1080)
- Dispositivi da 1,5 GB: 1,5 GB di RAM, risoluzione UI 1080p/FullHD (1920x1080), risoluzione video 2160p/UltraHD/4K (3840x2160)
- Altre situazioni in cui l'OEM ha definito il flag
ActivityManager.isLowRAMDevice()a causa di ulteriori vincoli di memoria.
Dispositivi TV normali
Questi dispositivi non sono soggetti a una situazione di pressione della memoria così significativa. Consideriamo che questi dispositivi abbiano le seguenti caratteristiche:
- ≥ 1,5 GB di RAM, UI 720p o 1080p e risoluzione video 1080p
- ≥ 2 GB di RAM, UI 1080p e risoluzione video 1080p o 2160p
Ciò non significa che le app non debbano preoccuparsi dell'utilizzo della memoria su questi dispositivi, in quanto alcuni usi impropri specifici della memoria possono comunque esaurire la memoria disponibile e avere un rendimento scarso.
Target di memoria sui dispositivi TV con poca RAM
Quando misuri la memoria su questi dispositivi, ti consigliamo vivamente di monitorare ogni sezione della memoria utilizzando il profiler di memoria di Android Studio. Le app TV devono profilare la memoria utilizzata e lavorare per inserire le proprie categorie al di sotto delle soglie definite in questa sezione.

Nella sezione Come viene conteggiata la memoria troverai una spiegazione dettagliata delle cifre della memoria riportate. Per la definizione delle soglie per le app TV, ci concentreremo su tre categorie di memoria:
- Anonima + Swap: composta da memoria di allocazione Java + nativa + stack in Android Studio.
- Grafica: segnalata direttamente nello strumento Profiler. Generalmente composte da texture grafiche.
- File: segnalato come categorie "Codice" + "Altri" in Android Studio.
Con queste definizioni, la tabella seguente indica il valore massimo che ogni tipo di gruppo di memoria deve utilizzare:
| Tipo di memoria | Purpose | Target di utilizzo (1 GB) |
|---|---|---|
| Anonimo + Swap (Java + nativo + stack) | Utilizzata per allocazioni, buffer multimediali, variabili e altre attività che richiedono molta memoria. | < 160 MB |
| Grafica | Utilizzato dalla GPU per le texture e i buffer correlati al display | 30-40 MB |
| File | Utilizzato per le pagine di codice e i file in memoria. | 60-80 MB |
La memoria totale massima (Anon+Swap + Graphics + File) non deve superare quanto segue:
- 280 MB di memoria utilizzata totale (Anon+Swap + Graphics + File) per dispositivi con 1 GB di RAM.
È vivamente consigliato di non superare:
- 200 MB di memoria utilizzata su (Anon+Swap + Graphics).
Memoria file
Come indicazioni generali per la memoria basata su file, tieni presente che:
- In generale, la memoria dei file viene gestita bene dalla gestione della memoria del sistema operativo.
- Al momento non abbiamo riscontrato che sia una delle cause principali della pressione della memoria.
Tuttavia, quando si ha a che fare con la memoria dei file in generale:
- Non includere librerie inutilizzate nella build e utilizza piccoli sottoinsiemi di librerie anziché quelle complete, se possibile.
- Non tenere aperti file di grandi dimensioni in memoria e chiudili non appena hai finito di utilizzarli.
- Riduci al minimo le dimensioni del codice compilato per le classi Java e Kotlin. Consulta la guida Riduci, offusca e ottimizza la tua app.
Consigli specifici per la TV
Questa sezione fornisce consigli specifici per ottimizzare la memoria utilizzata sui dispositivi TV.
Memoria video
Utilizza formati e risoluzioni delle immagini appropriati.
- Non caricare immagini con una risoluzione superiore a quella dell'interfaccia utente del dispositivo. Ad esempio, le immagini a 1080p devono essere ridimensionate a 720p su un dispositivo con UI a 720p.
- Utilizza bitmap basati sull'hardware quando possibile.
- Nelle librerie come Glide, attiva la funzionalità
Downsampler.ALLOW_HARDWARE_CONFIGche è disattivata per impostazione predefinita. L'attivazione di questa opzione evita la duplicazione delle bitmap che altrimenti si troverebbero sia nella memoria video che nella memoria anonima.
- Nelle librerie come Glide, attiva la funzionalità
- Evita rendering intermedi e rendering ripetuti
- Questi possono essere identificati con Android GPU Inspector:
- Nella sezione "Texture", cerca le immagini che rappresentano i passaggi verso il rendering finale anziché solo gli elementi che lo compongono. Si tratta in genere di un cosiddetto "rendering intermedio".
- Per le applicazioni SDK Android, spesso puoi rimuoverli utilizzando il
flag di layout
forceHasOverlappedRendering:falseper disattivare i rendering intermedi per questo layout. - Consulta la sezione Evitare rendering sovrapposti sui rendering sovrapposti come risorsa utile.
- Evita di caricare immagini segnaposto quando possibile, utilizza
@android:color/o@colorper le texture segnaposto. - Evita di comporre più immagini sul dispositivo quando la composizione potrebbe essere eseguita offline. Preferisci caricare immagini autonome anziché comporre immagini da quelle scaricate
- Segui la guida Gestione delle bitmap per gestire meglio le bitmap.
Anon+Swap memory
Anon+Swap è composto da allocazioni Native + Java + Stack nel profiler di memoria di Android Studio. Utilizza
ActivityManager.isLowMemoryDevice()
per verificare se il dispositivo ha problemi di memoria e adattati a questa situazione
seguendo queste linee guida.
- Media:
- Specifica una dimensione variabile per i buffer multimediali a seconda della RAM del dispositivo e della risoluzione di riproduzione video. In questo modo, la riproduzione video dovrebbe durare 1 minuto:
- 40-60 MB per 1 GB / 1080p
- 60-80 MB per 1,5 GB / 1080p
- 80-100 MB per 1,5 GB / 2160p
- 100-120 MB per 2 GB / 2160p
- Allocazioni di memoria per contenuti multimediali senza costi quando si cambia episodio per evitare aumenti della quantità totale di memoria anonima.
- Rilascia e interrompi immediatamente le risorse multimediali quando l'app viene
interrotta: utilizza i callback del ciclo di vita
dell'attività per gestire le risorse audio e video. Se non utilizzi un'app audio,
interrompi la riproduzione quando
onStop()si verifica nelle tue attività, salva tutto il lavoro che stai svolgendo e imposta le risorse in modo che vengano rilasciate. Per pianificare il lavoro di cui potresti aver bisogno in un secondo momento. Consulta la sezione Lavori e sveglie.- Puoi utilizzare i componenti sensibili al ciclo di vita
come
LiveDataeLifecycleOwnerper gestire le chiamate del ciclo di vita dell'attività. - Per rendere il tuo lavoro consapevole del ciclo di vita, puoi anche utilizzare le coroutine Kotlin e i flussi Kotlin.
- Puoi utilizzare i componenti sensibili al ciclo di vita
come
- Presta attenzione alla memoria del buffer durante la ricerca di video: gli sviluppatori
spesso allocano 15-60 secondi aggiuntivi di contenuti futuri durante la ricerca per avere
il video pronto per l'utente, ma questo crea un sovraccarico di memoria aggiuntivo.
In generale, non superare i 5 secondi di buffer futuro finché l'utente non ha
selezionato la nuova posizione del video. Se hai assolutamente bisogno di pre-bufferizzare
tempo aggiuntivo durante la ricerca, assicurati di:
- Alloca in anticipo il buffer di ricerca e riutilizzalo.
- La dimensione del buffer non deve superare 15-25 MB (a seconda della memoria del dispositivo).
- Specifica una dimensione variabile per i buffer multimediali a seconda della RAM del dispositivo e della risoluzione di riproduzione video. In questo modo, la riproduzione video dovrebbe durare 1 minuto:
- Allocazioni:
- Utilizza le indicazioni sulla memoria video per assicurarti di
non duplicare le immagini nella memoria anonima
- Le immagini spesso occupano la maggior parte della memoria, quindi la loro duplicazione può mettere a dura prova il dispositivo. Ciò è particolarmente vero durante la navigazione intensa delle visualizzazioni a griglia delle immagini.
- Rilascia le allocazioni eliminando i relativi riferimenti quando sposti le schermate: assicurati che non rimangano riferimenti a bitmap e oggetti.
- Utilizza le indicazioni sulla memoria video per assicurarti di
non duplicare le immagini nella memoria anonima
- Librerie:
- Allocazioni di memoria del profilo dalle librerie quando ne aggiungi di nuove, in quanto potrebbero caricare anche librerie aggiuntive, che potrebbero anche effettuare allocazioni e creare binding.
- Networking:
- Non eseguire chiamate di rete di blocco durante l'avvio dell'app, rallentano il tempo di avvio dell'applicazione e creano un sovraccarico di memoria aggiuntivo all'avvio, dove la memoria è particolarmente limitata dal caricamento dell'app. Mostra prima una schermata di caricamento o iniziale ed esegui le richieste di rete una volta che l'interfaccia utente è a posto.
Associazioni
I binding introducono un overhead di memoria aggiuntivo in quanto caricano in memoria altre applicazioni o aumentano il consumo di memoria dell'app associata (se è già in memoria) per facilitare la chiamata API. Di conseguenza, riduce la memoria disponibile per l'app in primo piano. Quando associ un servizio, fai attenzione a quando e per quanto tempo utilizzi l'associazione. Assicurati di rilasciare il binding non appena non è più necessario.
Binding tipici e best practice:
- API Play Integrity: utilizzata per verificare l'integrità del dispositivo
- Controlla l'integrità del dispositivo dopo la schermata di caricamento e prima della riproduzione dei contenuti multimediali
- Rilascia i riferimenti a PlayIntegrity
StandardIntegrityManagerprima di riprodurre i contenuti.
- Libreria Fatturazione Play: utilizzata per gestire
abbonamenti e acquisti tramite Google Play
- Inizializza la libreria dopo la schermata di caricamento e gestisci tutte le operazioni di fatturazione prima di riprodurre qualsiasi contenuto multimediale.
- Utilizza
BillingClient.endConnection()quando hai finito di utilizzare la libreria e sempre prima di riprodurre video o contenuti multimediali. - Utilizza
BillingClient.isReady()eBillingClient.getConnectionState()per verificare se il servizio è stato disconnesso nel caso in cui sia necessario eseguire nuovamente la fatturazione, quindi esegui di nuovoBillingClient.endConnection()al termine.
- GMS FontsProvider
- Preferisci utilizzare caratteri autonomi su dispositivi con poca RAM anziché utilizzare un fornitore di caratteri, poiché il download dei caratteri è costoso e FontsProvider assocerà i servizi per farlo.
- Libreria Assistente Google: a volte
utilizzata per la ricerca e la ricerca in-app, se possibile, sostituisci questa libreria.
- Per le app leanback: utilizza la sintesi vocale Gboard o la libreria androidx.leanback.
- Segui le linee guida per la ricerca per implementare la ricerca.
- Nota: leanback è ritirato e le app devono passare a TV Compose.
- Per le app Compose:
- Utilizza la sintesi vocale di Gboard per implementare la ricerca vocale.
- Implementa Watch Next per rendere rilevabili i contenuti multimediali nella tua app.
- Per le app leanback: utilizza la sintesi vocale Gboard o la libreria androidx.leanback.
Servizi in primo piano
I servizi in primo piano sono un tipo speciale di servizio legato a una notifica. Questa notifica viene visualizzata nella barra delle notifiche di smartphone e tablet, ma i dispositivi TV non hanno una barra delle notifiche nello stesso senso di questi dispositivi. Anche se i servizi in primo piano sono utili perché possono essere mantenuti in esecuzione mentre l'applicazione è in background, le app TV devono rispettare queste linee guida:
Su Android TV e Google TV, i servizi in primo piano sono consentiti solo per continuare a essere eseguiti dopo che l'utente esce dall'app:
- Per le app audio: i servizi in primo piano sono consentiti solo per continuare a essere eseguiti una volta che l'utente esce dall'app per continuare a riprodurre la traccia audio. Il servizio deve essere interrotto immediatamente dopo la fine della riproduzione audio.
- Per qualsiasi altra app: tutti i servizi in primo piano devono essere interrotti. una volta che l'utente esce dall'app, poiché non è presente alcuna notifica per informare l'utente che l'app è ancora in esecuzione e consuma risorse.
- Per i job in background, come l'aggiornamento dei consigli o dei video Da guardare, utilizza
WorkManager.
Lavoro e sveglie
WorkManager
è l'API Android all'avanguardia per la pianificazione di job ricorrenti in background.
WorkManager utilizzerà il nuovo
JobScheduler quando disponibile (SDK
23+) e il vecchio AlarmManager quando non lo è. Per le best practice per l'esecuzione di job pianificati sulla TV, segui questi
consigli:
- Evita di utilizzare le API
AlarmManagersu SDK 23 o versioni successive, in particolareAlarmManager.set(),AlarmManager.setExact()e metodi simili, in quanto non consentono al sistema di decidere il momento giusto per eseguire i job (ad esempio, quando il dispositivo è inattivo). - Sui dispositivi con poca RAM, evita di eseguire job a meno che non sia strettamente necessario. Se necessario,
utilizza WorkManager
WorkRequestsolo per aggiornare i consigli dopo la riproduzione e prova a farlo mentre l'app è ancora aperta. Definisci WorkManager
Constraintsper consentire al sistema di eseguire i job al momento opportuno:
Kotlin
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
Java
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
- Se devi eseguire regolarmente dei job (ad esempio, per aggiornare Watch Next in base all'attività di visualizzazione di contenuti di un utente nella tua app su un altro dispositivo), mantieni basso l'utilizzo della memoria mantenendo il consumo di memoria del job inferiore a 30 MB.
Considerazioni generali sulla memoria
Le seguenti linee guida forniscono informazioni generali sullo sviluppo di app per Android:
- Riduci le allocazioni di oggetti, ottimizza il riutilizzo degli oggetti e dealloca
tempestivamente gli oggetti inutilizzati.
- Non conservare riferimenti a oggetti, in particolare bitmap.
- Evita di utilizzare
System.gc()e chiamate di rilascio diretto della memoria in quanto interferiscono con la procedura di gestione della memoria del sistema: ad esempio, nei dispositivi che utilizzano zRAM, una chiamata forzata agc()può aumentare temporaneamente la memoria utilizzata a causa della compressione e della decompressione della memoria. - Utilizza
LazyListcome mostrato in un browser di cataloghi in Compose oRecyclerViewin Leanback UI Toolkit, ora ritirato, per riutilizzare le visualizzazioni e non ricreare gli elementi di elenco. - Memorizza nella cache locale gli elementi letti dai fornitori di contenuti esterni che è improbabile che cambino e definisci intervalli di aggiornamento che impediscano l'allocazione di memoria esterna aggiuntiva.
- Controlla la presenza di possibili perdite di memoria.
- Fai attenzione ai tipici casi di perdita di memoria, ad esempio riferimenti all'interno di thread anonimi, riallocazione di buffer video che non vengono mai rilasciati e altre situazioni simili.
- Utilizza il dump dell'heap per debuggare le perdite di memoria.
- Genera profili di base per ridurre al minimo la quantità di compilazione just-in-time necessaria durante l'esecuzione dell'app con avvio a freddo.
Informazioni sul recupero diretto della memoria
Quando un'applicazione Android TV richiede memoria e il sistema è sotto pressione, il kernel Linux, che è alla base di Android, potrebbe dover utilizzare il recupero diretto della memoria.
La procedura prevede la sospensione completa di qualsiasi thread di allocazione per attendere le pagine di memoria liberate. Ciò si verifica quando il recupero in background non è in grado di mantenere in modo proattivo un pool di memoria sufficiente.
Ciò può causare pause o scatti evidenti nell'esperienza utente, poiché il
sistema mette in pausa l'allocazione dei thread finché non viene resa disponibile memoria sufficiente. In
questo senso, l'allocazione dei thread non è limitata alle chiamate di codice dell'applicazione come
malloc(); ad esempio, la memoria deve essere allocata alla pagina nelle pagine di codice.
Riepilogo degli strumenti
- Utilizza lo strumento profiler di memoria di Android Studio
per controllare il consumo di memoria durante l'utilizzo.
- Utilizza heapdump per controllare allocazioni specifiche di oggetti e bitmap.
- Utilizza Native Memory Profiler per controllare le allocazioni non Java o Kotlin.
- Utilizza Android GPU Inspector per controllare le allocazioni grafiche.