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 richiede molte risorse, ad esempio giocando o guardando un video. Per migliorare l'esperienza utente, Android 8.0 (livello API 26) impone 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 affinché funzioni correttamente con le nuove limitazioni.

Panoramica

Molti servizi e app Android possono essere in esecuzione contemporaneamente. Ad esempio, un utente potrebbe giocare 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, maggiore è il carico sul sistema. Se ulteriori app o servizi sono in esecuzione in background, vengono caricati ulteriori carichi sul sistema, il che potrebbe compromettere l'esperienza utente; ad esempio, l'app di musica potrebbe essere arrestata improvvisamente.

Per ridurre le probabilità che si verifichino questi problemi, Android 8.0 pone delle limitazioni alle azioni che le app possono eseguire quando gli utenti non interagiscono direttamente con le app. Le app sono soggette a limitazioni in due modi:

  • Limitazioni dei servizi in background: quando un'app è inattiva, sono previsti dei limiti all'utilizzo dei servizi in background. 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 proprio manifest per la registrazione di trasmissioni implicite. Possono comunque registrarsi per queste trasmissioni in fase di runtime e utilizzare il manifest per registrarsi per trasmissioni e trasmissioni esplicite indirizzate specificatamente alla loro app.

Nella maggior parte dei casi, le app possono aggirare queste limitazioni utilizzando i job di JobScheduler. Questo approccio consente a un'app di eseguire il funzionamento quando l'app non è in esecuzione attiva, ma offre comunque al sistema la possibilità di pianificare questi job in un modo che non influisca sull'esperienza utente. Android 8.0 offre diversi miglioramenti a JobScheduler che semplificano la sostituzione di servizi e la trasmissione dei ricevitori con job programmati; per ulteriori informazioni, consulta i miglioramenti di JobScheduler.

Limitazioni del servizio in background

I servizi in esecuzione in background possono consumare risorse del dispositivo, determinando un'esperienza utente peggiore. Per mitigare questo problema, il sistema applica una serie di limitazioni ai servizi.

Il sistema fa una distinzione tra app in primo piano e 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 servizi.) Un'app è considerata in primo piano se si verifica una delle seguenti condizioni:

  • Presenta 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 mediante associazione 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.

Quando un'app è in primo piano, può creare ed eseguire liberamente sia servizi in primo piano che in background. Quando un'app passa in background, ha una finestra di alcuni minuti in cui è comunque autorizzata a 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 alcuni casi, un'app in background viene inserita in una lista consentita temporanea per diversi minuti. Quando un'app è nella lista consentita, può avviare 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 di JobScheduler. Ad esempio, CoolPhotoApp 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. Per eseguire la migrazione ad Android 8.0 (livello API 26), lo sviluppatore sostituisce il servizio in background con un job pianificato, che viene avviato periodicamente, esegue una query sul server e poi si chiude.

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 in modo da 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 trasmissioni, il ricevitore dell'app consuma risorse ogni volta che viene inviata la trasmissione. Ciò può causare problemi se troppe app si registrano per ricevere trasmissioni in base agli eventi di sistema. Un evento di sistema che attiva una trasmissione può far sì che tutte queste app consumino risorse in rapida successione, compromettendo l'esperienza utente. Per ridurre il problema, Android 7.0 (livello API 24) ha posto limitazioni alle trasmissioni, come descritto in Ottimizzazione in background. Android 8.0 (livello API 26) rende queste limitazioni più rigorose.

  • Le app destinate ad Android 8.0 o versioni successive non possono più registrare ricevitori per trasmissioni implicite nel 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 i listener registrati in tutte le app, per comunicare che è stato sostituito un pacchetto sul dispositivo. Poiché la trasmissione è implicita, non verrà inviata ai 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 pacco è stato sostituito, verrà consegnato a destinatari registrati nel file manifest.
  • Le app possono continuare a registrarsi per trasmissioni esplicite nei loro 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 limitazione, poiché vengono inviate solo alle app firmate con lo stesso certificato, non a tutte le app sul dispositivo.

In molti casi, le app che in precedenza erano registrate per una trasmissione implicita possono ricevere funzionalità simili utilizzando un job JobScheduler. Ad esempio, di tanto in tanto un'app social per le foto potrebbe dover pulire i propri dati e preferire questa operazione quando il dispositivo è collegato a un caricabatterie. In precedenza, l'app registrava un ricevitore per ACTION_POWER_CONNECTED nel file manifest. Quando riceveva la trasmissione, controllava se era necessario eseguire 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 l'app 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 che vengono eseguiti in background mentre l'app è inattiva, devi sostituirli. Le possibili soluzioni sono:

  • Se la tua app deve creare un servizio in primo piano mentre è in background, utilizza il metodo startForegroundService() anziché startService().
  • Se il servizio è visibile per l'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 esegue operazioni immediatamente visibili all'utente, in genere dovresti essere in grado di utilizzare un job pianificato.
  • Utilizza FCM per riattivare l'applicazione in modo selettivo quando si verificano eventi di rete, anziché eseguire il polling in background.
  • Rimanda il lavoro in background fino a quando l'applicazione è naturalmente in primo piano.

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

  • Crea il destinatario 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.