Limiti di esecuzione in background

Ogni volta che un'app viene eseguita in background, consuma alcune delle risorse limitate del dispositivo, come la RAM. Ciò può compromettere l'esperienza utente, soprattutto se l'utente utilizza un'app che consuma molta risorse, ad esempio sta riproducendo un gioco o guardando un video. Per migliorare l'esperienza utente, Android 8.0 (livello API 26) impone delle limitazioni su ciò che le app possono fare durante l'esecuzione in background. Questo documento descrive le modifiche al sistema operativo e come puoi aggiornare la tua app in modo che funzioni correttamente con le nuove limitazioni.

Panoramica

Molti servizi e app Android possono essere eseguiti contemporaneamente. Ad esempio, un utente potrebbe giocare a un gioco in una finestra mentre naviga sul web in un'altra finestra e utilizzare una terza app per riprodurre musica. Maggiore è il numero di app in esecuzione contemporaneamente, più carico viene applicato al sistema. Se ulteriori app o servizi sono in esecuzione in background, vengono caricati ulteriori carichi sul sistema, il che potrebbe comportare un'esperienza utente scadente; ad esempio, l'app di musica potrebbe essere arrestata improvvisamente.

Per ridurre le probabilità di questi problemi, Android 8.0 applica limitazioni alle azioni che le app possono eseguire quando gli utenti non interagiscono direttamente con le app. Le app sono limitate in due modi:

  • Limitazioni dei servizi in background: quando un'app è inattiva, il suo utilizzo dei servizi in background sono soggetti a limiti. Questo non si applica ai servizi in primo piano, più visibili all'utente.

  • Limitazioni di trasmissione: con alcune eccezioni, le app non possono utilizzare il relativo file manifest per la registrazione per le trasmissioni implicite. Possono comunque registrarsi per queste trasmissioni in fase di runtime e utilizzare il manifest per registrarsi per trasmissioni e trasmissioni esplicite mirate specificamente alla propria app.

Nella maggior parte dei casi, le app possono aggirare queste limitazioni utilizzando i job di JobScheduler. Questo approccio consente a un'app di organizzare il lavoro quando non è in esecuzione attiva, ma offre comunque al sistema la possibilità di pianificare questi job in modo che non influiscano sull'esperienza utente. Android 8.0 offre diversi miglioramenti a JobScheduler che semplificano la sostituzione di servizi e la trasmissione dei ricevitori con job pianificati. Per saperne di più, consulta la sezione Miglioramenti di JobScheduler.

Limitazioni del servizio in background

I servizi in esecuzione in background possono consumare risorse del dispositivo, con un conseguente potenziale peggioramento dell'esperienza utente. Per limitare questo problema, il sistema applica una serie di limitazioni ai servizi.

Il sistema distingue tra app in primo piano e app in in background. La definizione di background ai fini delle limitazioni dei servizi è diversa dalla definizione utilizzata dalla gestione della memoria; un'app potrebbe essere in background per quanto riguarda la gestione della memoria, ma in primo piano per quanto riguarda la sua capacità di avviare i servizi. Un'app è considerata in primo piano se si verifica una delle seguenti condizioni:

  • Ha un'attività visibile, indipendentemente dal fatto che sia iniziata o in pausa.
  • Ha un servizio in primo piano.
  • Un'altra app in primo piano è connessa all'app, vincolandosi a uno dei suoi servizi o utilizzando uno dei suoi fornitori di contenuti. Ad esempio, l'app è in primo piano se un'altra app si associa a:
    • IME
    • Servizio di carta da parati
    • Listener di notifica
    • Servizio vocale o di testo

Se nessuna di queste condizioni è vera, l'app è considerata in background.

Mentre un'app è in primo piano, può creare ed eseguire liberamente i servizi in primo piano e in background. Quando un'app passa in background, ha un periodo di diversi minuti durante il quale può ancora creare e utilizzare i servizi. Al termine di questa finestra, l'app è considerata inattiva. Al momento, il sistema interrompe i servizi in background dell'app, proprio come se l'app avesse chiamato i metodi Service.stopSelf() dei servizi.

In determinate circostanze, un'app in background viene inserita in una lista consentita temporanea per diversi minuti. Quando un'app è nella lista consentita, può lanciare servizi senza limitazioni e i relativi servizi in background possono essere eseguiti. Un'app viene inserita nella lista consentita quando gestisce un'attività visibile all'utente, ad esempio:

In molti casi, la tua app può sostituire i servizi in background con job JobScheduler. Ad esempio, CoolFotoApp deve verificare se l'utente ha ricevuto foto condivise da amici, anche se l'app non è in esecuzione in primo piano. In precedenza, l'app utilizzava un servizio in background che controllava lo spazio di archiviazione sul cloud dell'app. Per eseguire la migrazione ad Android 8.0 (livello API 26), lo sviluppatore sostituisce il servizio in background con un job programmato, che viene avviato periodicamente, esegue query sul server e quindi chiude la sessione.

Prima di Android 8.0, il modo abituale per creare un servizio in primo piano era creare un servizio in background e poi promuoverlo in primo piano. Con Android 8.0 c'è una complicazione: il sistema non consente a un'app in background di creare un servizio in background. Per questo motivo, Android 8.0 introduce il nuovo metodo startForegroundService() per avviare un nuovo servizio in primo piano. Dopo che il sistema ha creato il servizio, l'app ha cinque secondi per chiamare il metodo [startForeground()](/reference/android/app/Service#startForeground(int, android.app.Notification) del servizio) per mostrare la notifica visibile all'utente del nuovo servizio. Se l'app non chiama startForeground() entro il limite di tempo, il sistema interrompe il servizio e dichiara che l'app è ANR.

Limitazioni di trasmissione

Se un'app si registra per ricevere broadcast, il destinatario dell'app consuma risorse ogni volta che viene inviato il broadcast. Ciò può causare problemi se si registrano troppe app per ricevere broadcast in base agli eventi di sistema. Un evento di sistema che attiva una trasmissione può causare il consumo rapido di risorse da parte di tutte queste app, compromettendo l'esperienza utente. Per limitare questo problema, Android 7.0 (livello API 24) ha applicato limitazioni alle trasmissioni, come descritto in Ottimizzazione in background. Android 8.0 (livello API 26) rende queste limitazioni più restrittive.

  • Le app destinate ad Android 8.0 o versioni successive non possono più registrare i ricevitori di broadcast per le trasmissioni implicite nel loro file manifest, a meno che la trasmissione non sia limitata specificamente a quell'app. Una trasmissione implicita è una trasmissione che non ha come target un componente specifico all'interno di un'app. Ad esempio, ACTION_PACKAGE_REPLACED viene inviato a tutti gli ascoltatori registrati in tutte le app per comunicare che un pacchetto sul dispositivo è stato sostituito. Poiché la trasmissione è implicita, non verrà inviata a ricevitori registrati con manifest nelle app destinate ad Android 8.0 o versioni successive. Anche ACTION_MY_PACKAGE_REPLACED è una trasmissione implicita, ma, poiché viene inviata solo all'app il cui pacchetto è stato sostituito, verrà inviata a destinatari registrati con manifest.
  • Le app possono continuare a registrarsi per la trasmissione esplicite nei relativi file manifest.
  • Le app possono usare Context.registerReceiver() in fase di runtime per registrare un ricevitore per qualsiasi trasmissione, implicita o esplicita.
  • Le trasmissioni che richiedono un'autorizzazione di firma sono esenti da questa restrizione, poiché queste trasmissioni vengono inviate solo alle app firmate con lo stesso certificato, non a tutte le app sul dispositivo.

In molti casi, le app precedentemente registrate per una trasmissione implicita possono ottenere una funzionalità simile utilizzando un job JobScheduler. Ad esempio, di tanto in tanto un'app di foto social potrebbe dover eseguire la pulizia dei suoi dati e preferirebbe farlo quando il dispositivo è collegato a un caricabatterie. In precedenza, l'app registrava un ricevitore per ACTION_POWER_CONNECTED nel file manifest. Quando l'app riceveva la trasmissione, controllava se era necessaria la pulizia. Per eseguire la migrazione ad Android 8.0 o versioni successive, l'app rimuove il destinatario dal relativo file manifest. L'app pianifica invece un job di pulizia che viene eseguito quando il dispositivo è inattivo e in carica.

Guida alla migrazione

Per impostazione predefinita, queste modifiche interessano solo le app destinate ad Android 8.0 (livello API 26) o versioni successive. Tuttavia, gli utenti possono attivare queste limitazioni per qualsiasi app dalla schermata Impostazioni, anche se ha come target un livello API inferiore a 26. Potresti dover aggiornare l'app per rispettare le nuove limitazioni.

Controlla in che modo la tua app utilizza i servizi. Se la tua app si basa su servizi eseguiti in background mentre l'app è inattiva, dovrai sostituirli. Le possibili soluzioni includono:

  • Se l'app deve creare un servizio in primo piano mentre è in background, utilizza il metodo startForegroundService() anziché startService().
  • Se il servizio viene notato dall'utente, impostalo come servizio in primo piano. Ad esempio, un servizio che riproduce l'audio deve essere sempre un servizio in primo piano. Crea il servizio utilizzando il metodo startForegroundService() anziché startService().
  • Trova un modo per duplicare la funzionalità del servizio con un job pianificato. Se il servizio non rende immediatamente visibile all'utente, in genere dovresti essere in grado di utilizzare invece un job programmato.
  • Utilizza FCM per riattivare selettivamente l'applicazione quando si verificano eventi di rete, anziché eseguire il polling in background.
  • Rimanda il lavoro in background finché l'applicazione non è naturalmente in primo piano.

Esamina i ricevitori di trasmissione definiti nel file manifest dell'app. Se il manifest dichiara un ricevitore per una trasmissione implicita interessata, devi sostituirlo. Le possibili soluzioni includono:

  • Crea il ricevitore in fase di runtime chiamando Context.registerReceiver(), anziché dichiararlo nel manifest.
  • Utilizza un job pianificato per verificare la condizione che avrebbe attivato la trasmissione implicita.