Quando si avvia un componente dell'applicazione e l'applicazione non ha altri componenti in esecuzione, il sistema Android avvia un nuovo processo Linux per l'applicazione con un singolo thread di dell'esecuzione. Per impostazione predefinita, tutti i componenti della stessa applicazione vengono eseguiti nello stesso processo e nello stesso thread, chiamato thread main.
Se un componente dell'applicazione viene avviato ed esiste già un processo per quell'applicazione, perché un altro componente dell'applicazione è già stato avviato, il componente viene avviato all'interno del processo e utilizza lo stesso thread di esecuzione. Tuttavia, puoi organizzare componenti diversi nell'applicazione in modo che vengano eseguiti in processi separati e puoi creare altri per qualsiasi processo.
Questo documento illustra il funzionamento di processi e thread in un'applicazione Android.
Processi
Per impostazione predefinita, tutti i componenti di un'applicazione vengono eseguiti nello stesso processo e la maggior parte delle applicazioni non devi cambiarlo. Tuttavia, se scopri che devi controllare quale processo hai , puoi farlo nel file manifest.
La voce del file manifest per ogni tipo di elemento componente (<activity>
, <service>
, <receiver>
e <provider>
) supporta un attributo android:process
che può specificare un
durante l'esecuzione del componente. Puoi impostare questo attributo in modo che ogni componente venga eseguito
nel proprio processo o in modo che alcuni componenti condividano un processo e altri no.
Puoi anche impostare
android:process
in modo che i componenti di applicazioni diverse vengano eseguiti
processo, a condizione che le applicazioni condividano lo stesso ID utente Linux e siano firmate con
gli stessi certificati.
La <application>
supporta anche un attributo android:process
, che puoi utilizzare per impostare un
predefinito valido per tutti i componenti.
Android potrebbe decidere di arrestare un processo a un certo punto, quando le risorse come richiesto da altri processi che servino più immediatamente l'utente. Applicazione e i componenti in esecuzione nel processo chiuso vengono di conseguenza eliminati. Viene avviato un processo di nuovo per quei componenti quando c'è lavoro da fare.
Nel decidere quali processi arrestare, il sistema Android ne valuta la relativa importanza per l'utente. Ad esempio, chiude più facilmente un processo che ospita attività che visibili sullo schermo, rispetto a un processo che ospita attività visibili. La decisione di decidere se terminare un processo, perciò dipende dallo stato dei componenti in esecuzione in quel processo.
I dettagli del ciclo di vita del processo e la sua relazione con gli stati dell'applicazione sono trattati in Processi e ciclo di vita delle app.
Thread
Quando viene avviata un'applicazione, il sistema crea un thread di esecuzione per l'applicazione,
chiamato thread principale. Questo thread è molto importante perché si occupa dell'invio di eventi
i widget appropriati dell'interfaccia utente, inclusi gli eventi di disegno. È inoltre possibile
quasi sempre il thread in cui l'applicazione interagisce con i componenti
da android.widget
e dal toolkit dell'interfaccia utente di Android
android.view
di pacchetti.
Per questo motivo, a volte il thread principale
chiamato thread UI. Tuttavia, in circostanze speciali, l'elemento principale
potrebbe non essere il rispettivo thread UI. Per ulteriori informazioni, consulta la sezione Thread
annotazioni.
Il sistema non crea un thread separato per ogni istanza di un componente. Tutti
i componenti eseguiti nello stesso processo vengono creati nel thread dell'interfaccia utente, mentre le chiamate di sistema
ogni componente viene inviato da quel thread. Di conseguenza, i metodi che rispondono al sistema
di callback, ad esempio onKeyDown()
per segnalare le azioni degli utenti o un metodo di callback del ciclo di vita, sempre eseguite nel thread dell'interfaccia utente del processo.
Ad esempio, quando l'utente tocca un pulsante sullo schermo, il thread dell'interfaccia utente dell'app invia il token touch del widget, che a sua volta imposta lo stato di pressione e invia una richiesta di invalidazione a la coda degli eventi. Il thread dell'interfaccia utente elimina la richiesta e comunica al widget di ripetere il disegno per trovare le regole.
Se non implementi correttamente l'applicazione, questo modello a thread singolo può produrre prestazioni scarse quando l'app esegue un lavoro intensivo in risposta all'interazione dell'utente. Esecuzione di operazioni lunghe nel thread dell'interfaccia utente, ad esempio l'accesso alla rete del database, blocca l'intera UI. Quando il thread è bloccato, non è possibile inviare eventi, inclusi gli eventi di disegno.
Dal punto di vista dell'utente, sembra bloccarsi. Peggio ancora, se il thread dell'interfaccia utente viene bloccato per più di qualche secondo, all'utente viene presentato "l'applicazione non risposta" (ANR). L'utente potrebbe quindi decidere di uscire dalla tua applicazione o addirittura di disinstallarla li annotino.
Tieni presente che il toolkit per la UI di Android non è l'utilizzo dei thread. Quindi, non manipolare la tua UI da un thread worker. Eseguire tutte le modifiche dell'interfaccia utente dall'interfaccia utente . Il modello a thread singolo di Android prevede due regole:
- Non bloccare il thread dell'interfaccia utente.
- Non accedere al toolkit per la UI di Android dall'esterno del thread dell'interfaccia utente.
Thread worker
Grazie a questo modello a thread singolo, è fondamentale la reattività del tuo dell'applicazione in modo che non blocchi il thread della UI. Se devi eseguire operazioni che non sono istantanee, assicurati di farlo in background a parte i thread worker. Ricorda che puoi aggiornare l'interfaccia utente solo da thread nella UI, o thread principale.
Per aiutarti a rispettare queste regole, Android offre diversi modi per accedere al thread dell'interfaccia utente da altri i thread. Ecco un elenco di metodi che possono essere utili:
L'esempio seguente utilizza View.post(Runnable)
:
Kotlin
fun onClick(v: View) { Thread(Runnable { // A potentially time consuming task. val bitmap = processBitMap("image.png") imageView.post { imageView.setImageBitmap(bitmap) } }).start() }
Java
public void onClick(View v) { new Thread(new Runnable() { public void run() { // A potentially time consuming task. final Bitmap bitmap = processBitMap("image.png"); imageView.post(new Runnable() { public void run() { imageView.setImageBitmap(bitmap); } }); } }).start(); }
Questa implementazione è sicura per i thread, perché l'operazione in background viene eseguita da un thread separato
mentre ImageView
viene sempre manipolato dal thread dell'interfaccia utente.
Tuttavia, con l'aumento della complessità dell'operazione, questo tipo di codice può diventare complicato e
difficili da gestire. Per gestire interazioni più complesse con un thread di lavoro, puoi prendere in considerazione
utilizzando Handler
nel tuo thread di lavoro
per elaborare i messaggi consegnati dal thread dell'interfaccia utente. Per una spiegazione completa su come
pianificare le operazioni sui thread in background e comunicare con i thread dell'interfaccia utente, vedi
Panoramica del lavoro in background.
Metodi con protezione dei thread
In alcune situazioni, i metodi implementati vengono chiamati da più thread e perciò devono essere scritti in modo da essere siccome i thread.
Questo vale principalmente per i metodi che possono essere chiamati da remoto, come i metodi in un servizio associato. Quando una chiamata su un
implementato in un IBinder
ha origine nello stesso processo in cui
IBinder
è in esecuzione, il metodo viene eseguito nel thread del chiamante.
Tuttavia, quando la chiamata ha origine in un altro processo, il metodo viene eseguito in un thread scelto da
un pool di thread che il sistema gestisce nello stesso processo di IBinder
.
Non viene eseguito nel thread dell'interfaccia utente del processo.
Ad esempio, mentre il servizio
Il metodo onBind()
viene chiamato dal thread dell'interfaccia utente del
di un servizio, metodi implementati nell'oggetto che onBind()
restituisce, ad esempio
che implementa i metodi di chiamata di procedura remota (RPC), viene richiamata dai thread
in piscina. Poiché un servizio può avere più di un client, è possibile coinvolgere più di un thread del pool
lo stesso metodo IBinder
contemporaneamente, quindi i metodi IBinder
devono essere
implementato in modo da essere sicuro per i thread.
Analogamente, un fornitore di contenuti può ricevere richieste di dati che hanno origine da altri processi.
ContentResolver
e ContentProvider
nascondono i dettagli di come viene gestita la comunicazione tra processi (IPC),
ma i metodi ContentProvider
che rispondono a queste richieste: i metodi
query()
,
insert()
,
delete()
,
update()
,
e getType()
: sono
chiamati da un pool di thread nel processo del fornitore di contenuti, non dalla UI
thread per il processo. Poiché questi metodi possono essere chiamati da un numero qualsiasi di thread nella
e allo stesso tempo, anch'essi devono essere implementati per poter essere sicuri per i thread.
Inter-elabora la comunicazione
Android offre un meccanismo per IPC che utilizza RPC, in cui un metodo viene chiamato da un'attività o da un'altra applicazione ma eseguita in remoto in un altro processo, con qualsiasi risultato restituito al chiamante. Ciò comporta la scomposizione di una chiamata di metodo e dei suoi dati a un livello che il sistema operativo può comprenderlo, trasmettendolo dal processo locale e dallo spazio di indirizzi al processo remoto e dello spazio degli indirizzi IP, per poi riassemblare e rieseguire la chiamata da lì.
I valori restituiti saranno quindi trasmesse nella direzione opposta. Android fornisce tutto il codice per eseguire queste IPC transazioni, per consentirti di concentrarti sulla definizione e sull'implementazione dell'interfaccia di programmazione RPC.
Per eseguire l'IPC, la tua applicazione deve essere associata a un servizio utilizzando bindService()
. Per ulteriori informazioni, consulta la Panoramica dei servizi.