Processi e ciclo di vita dell'app

Nella maggior parte dei casi, ogni applicazione Android viene eseguita nel proprio processo Linux. Questo processo viene creato per l'applicazione quando parte del codice deve essere eseguita e rimane in esecuzione fino a quando il sistema non ha bisogno di recuperare la memoria per l'utilizzo da parte di altre applicazioni, che non è più necessaria.

Una caratteristica insolita e fondamentale di Android è che la vita di un processo non è direttamente controllata dall'applicazione stessa. Viene invece determinato dal sistema attraverso una combinazione di parti dell'applicazione che il sistema sa essere in esecuzione, quanto sono importanti questi elementi per l'utente e la quantità di memoria complessiva disponibile nel sistema.

È importante che gli sviluppatori di applicazioni comprendano in che modo i diversi componenti dell'applicazione (in particolare Activity, Service e BroadcastReceiver) influiscono sulla durata del processo dell'applicazione. L'utilizzo non corretto di questi componenti può causare l'interruzione da parte del sistema del processo dell'applicazione mentre svolge un lavoro importante.

Un esempio comune di bug del ciclo di vita di un processo è un elemento BroadcastReceiver che avvia un thread quando riceve un valore Intent nel metodo BroadcastReceiver.onReceive() e poi restituisce la funzione. Una volta restituito, il sistema considera che BroadcastReceiver non è più attivo e il suo processo di hosting non è più necessario, a meno che non siano attivi altri componenti dell'applicazione.

Quindi, il sistema può terminare il processo in qualsiasi momento per recuperare memoria e, in questo modo, termina il thread generato in esecuzione nel processo. In genere, la soluzione a questo problema consiste nel pianificare una JobService da BroadcastReceiver, in modo che il sistema sappia che è in corso un lavoro attivo nel processo.

Per determinare quali processi terminare quando la memoria è insufficiente, Android colloca ogni processo in una gerarchia di importanza basata sui componenti in esecuzione al loro interno e sullo stato di tali componenti. Di seguito sono elencati i tipi di processo in ordine di importanza:

  1. Un processo in primo piano è un processo necessario per ciò che l'utente sta attualmente facendo. Diversi componenti dell'applicazione possono far sì che il relativo processo di contenimento venga considerato in primo piano in modi diversi. Un processo è considerato in primo piano se si verifica una delle seguenti condizioni:
  2. Nel sistema sono presenti solo pochi processi di questo tipo, che vengono eliminati come ultima risorsa solo se la memoria è talmente bassa che neanche questi processi possono continuare a essere eseguiti. In genere, se questo accade, il dispositivo ha raggiunto uno stato di paging della memoria, quindi questa azione è necessaria per mantenere reattiva l'interfaccia utente.

  3. Un processo visibile sta svolgendo un lavoro di cui l'utente è attualmente a conoscenza, pertanto l'interruzione del processo ha un notevole impatto negativo sull'esperienza utente. Un processo viene considerato visibile nelle seguenti condizioni:
    • È in esecuzione un Activity visibile all'utente sullo schermo, ma non in primo piano (è stato chiamato il suo metodo onPause()). Questo potrebbe verificarsi, ad esempio, se la Activity in primo piano viene visualizzata come una finestra di dialogo che consente di visualizzare l'elemento Activity precedente.
    • Ha un Service in esecuzione come servizio in primo piano, tramite Service.startForeground() (che chiede al sistema di considerare il servizio come qualcosa di cui l'utente è a conoscenza o essenzialmente come se fosse visibile).
    • Ospita un servizio che il sistema utilizza per una particolare funzionalità di cui l'utente è a conoscenza, ad esempio uno sfondo animato o un servizio di metodo di immissione.

    Il numero di questi processi in esecuzione nel sistema è meno limitato rispetto ai processi in primo piano, ma è comunque relativamente controllato. Questi processi sono considerati estremamente importanti e non vengono terminati, a meno che ciò non sia necessario per mantenere in esecuzione tutti i processi in primo piano.

  4. Un processo di servizio è un processo in possesso di un Service che è stato avviato con il metodo startService(). Sebbene questi processi non siano direttamente visibili all'utente, in genere svolgono operazioni che interessano all'utente, ad esempio il caricamento o il download di dati di rete in background, quindi il sistema mantiene questi processi sempre in esecuzione a meno che non ci sia memoria sufficiente per conservare tutti i processi visibili e in primo piano.

    I servizi che sono in esecuzione da molto tempo (ad esempio 30 minuti o più) potrebbero subire un declassamento di importanza per consentire al loro processo di passare all'elenco memorizzato nella cache.

    I processi che devono essere eseguiti per un lungo periodo possono essere creati con setForeground. Se si tratta di un processo periodico che richiede un tempo di esecuzione rigoroso, può essere pianificato tramite AlarmManager. Per ulteriori informazioni, consulta Assistenza per i lavoratori a lunga esecuzione. Ciò consente di evitare le situazioni in cui i servizi a lunga esecuzione che utilizzano risorse eccessive, ad esempio con perdite di memoria, impediscono al sistema di offrire una buona esperienza utente.

  5. Un processo memorizzato nella cache è un processo attualmente non necessario, quindi il sistema è libero di terminarlo secondo necessità quando risorse come la memoria sono necessarie altrove. In un sistema che funziona normalmente, questi sono gli unici processi coinvolti nella gestione delle risorse.

    Un sistema in esecuzione ha sempre a disposizione più processi memorizzati nella cache, per un passaggio efficiente da un'applicazione all'altra, e interrompe regolarmente le app memorizzate nella cache secondo necessità. Solo in situazioni molto critiche il sistema arriva a un punto in cui tutti i processi memorizzati nella cache vengono interrotti e deve iniziare a terminare quelli del servizio.

    Poiché i processi memorizzati nella cache possono essere interrotti dal sistema in qualsiasi momento, le app devono interrompere il funzionamento mentre sono in stato memorizzato nella cache. Se le operazioni critiche per l'utente devono essere eseguite dall'app, l'app deve usare le API precedenti per eseguire le operazioni da uno stato di processo attivo.

    I processi memorizzati nella cache spesso contengono una o più istanze Activity che non sono attualmente visibili all'utente (il loro metodo onStop() è stato chiamato ed è stato restituito). Se l'azienda implementa correttamente il suo ciclo di vita di Activity quando il sistema arresta questi processi, l'esperienza dell'utente non cambia quando torna a quell'app. Quando l'attività associata viene ricreata in un nuovo processo, è possibile ripristinare lo stato salvato in precedenza. Tieni presente che non è garantito che onDestroy() venga richiamato nel caso in cui un processo venga interrotto dal sistema. Per maggiori dettagli, vedi Activity.

    A partire da Android 13, un processo dell'app potrebbe ricevere un tempo di esecuzione limitato o nullo finché non entra in uno degli stati del ciclo di vita attivi precedenti.

    I processi memorizzati nella cache vengono mantenuti in un elenco. Il criterio di ordinamento esatto per questo elenco è un dettaglio di implementazione della piattaforma. In genere, cerca di mantenere processi più utili, come quelli che ospitano l'applicazione Home dell'utente o l'ultima attività visualizzata dall'utente, prima di altri tipi di processi. È possibile applicare anche altri criteri per l'interruzione dei processi, ad esempio l'impostazione di limiti rigidi per il numero di processi consentiti o la limitazione del tempo per cui un processo può rimanere continuamente nella cache.

Per decidere come classificare un processo, il sistema basa la propria decisione sul livello più importante trovato tra tutti i componenti attualmente attivi nel processo. Consulta la documentazione relativa a Activity, Service e BroadcastReceiver per maggiori dettagli su come ognuno di questi componenti contribuisce al ciclo di vita complessivo di un processo e dell'applicazione.

La priorità di un processo può essere aumentata anche in base ad altre dipendenze del processo. Ad esempio, se il processo A è associato a un elemento Service con il flag Context.BIND_AUTO_CREATE o utilizza un ContentProvider nel processo B, la classificazione del processo B è sempre importante quanto il processo A.