IT knowledge base
CTRL+F per cercare la tua parola chiave

Come Android avvia MainActivity

Di recente ho fatto alcune ricerche sul metodo main() in Java e su come funge da punto di ingresso per qualsiasi applicazione Java. Questo mi ha fatto pensare, che dire delle app Android? Hanno un metodo principale? Come vengono caricati? Cosa succede dietro le quinte prima che venga eseguito onCreate()? Michael Bailey è entrato nei dettagli su come funziona il thread principale, quindi questa è una rapida panoramica del suo discorso più informazioni aggiuntive dall'Android Open Source Project (AOSP).
In questo articolo vedremo:
  1. Cosa succede dal fare clic sull'icona dell'applicazione all'avvio di MainActivity
  2. Troviamo il metodo principale dell'applicazione e scopriamo come il thread principale (alias UI, alias Main Thread) ottiene il suo scopo.
  3. Diamo un'occhiata al ruolo che Looper & Handler svolgono nel trasmettere messaggi che alla fine portano alla creazione della tua attività.

Cosa succede all'avvio dell'applicazione

Quando si avvia qualsiasi applicazione, c'è molto da fare a livello di kernel, come il bootstrap di Zygote, il caricamento di classi nella JVM e, per la JVM, trovare il metodo static void main (String args []) e chiamarlo. Nel caso di Android, la JVM trova il metodo principale main() in ActivityThread. Quindi chiama main(), dopodiché il kernel trasferisce il controllo alla tua applicazione. Quindi abbiamo trovato il punto di ingresso - ActivityThread, ma prima di addentrarci in dettaglio, diamo un'occhiata alla roadmap del processo per visualizzare l'intera attività.

1 Schema di lancio dell'applicazione

Ci sono circa 15 passaggi tra la chiamata di main() e onCreate() nella nostra MainActivity e li esamineremo in questo articolo. La Figura 1 illustra un diagramma di avvio dell'applicazione generale che mostra le varie classi di interazione dall'alto e il corrispondente concatenamento del metodo. I passaggi sono numerati e quando mi riferisco ad essi userò la seguente notazione Process3 o Process14
Immagine
Figura 1: Schema di avvio di un'applicazione in passaggi dalla chiamata principale () a onCreate () in MainActivity

2. Classe ActivityThread

La classe ActivityThread ha poco più di 6500 righe. Per brevità, ho identificato le parti che sono più importanti per noi. Diamo un'occhiata a cosa fa questa classe e il suo metodo principale associato per avviare la nostra attività.
/**
* Code retrieved from https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java
* Modifications are indicated in the comments
*/
public static void main(String[] args) {
  //Modification - Removed unrelated initializers. 
  //Android initializes some tracers, event loggers, enviroment initializers, trusted certificates and updates the process' state
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        // More logging
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
}
Figura 2: il metodo main() in ActivityThread, che funge da punto di ingresso per avviare l'applicazione.
Come puoi vedere nel codice, il metodo main() fa tre cose importanti:
1. Prepara il Looper principale (MainLooper) (Processo 2)
2. Configurazione del gestore (processo 4)
3. Chiamare il metodo Looper.loop() nel thread principale (MainThread) (Processo 6)

2.1 Preparazione del crochet principale (processo 2-3)

Il looper principale viene impostato chiamando Looper.prepareMainLooper() (vedi riga 8 nel codice). Questo contrassegna il thread casuale corrente che esegue tutto il lavoro di chiamata del metodo main() come thread dell'applicazione principale. Questo è esattamente come e dove viene definito il famoso thread principale per un'applicazione Android!

2.2 Gestore della chiamata (processo 4-5)

All'interno della classe ActivityThread c'è una classe interna privata H, sì, proprio così, solo H, che eredita dalla classe Handler (vedi Figure 4 e 7). Nella riga 12, l'istanza del gestore H è impostata come gestore principale del thread. Ciò che è molto interessante sapere sulla classe H, come vedrai tu stesso in seguito, è che contiene oltre 50 definizioni di stato/evento in cui può risiedere la tua applicazione, come LAUNCH_ACTIVITY, PAUSE_ACTIVITY, BIND_SERVICE, ecc.

2.3 Chiamare il metodo loop() di Looper (Processo 6-7)

Dopo aver assegnato un thread principale nello stesso thread principale, in modo da poterci fare qualcosa, viene chiamato il metodo Looper.loop() (vedi riga 20). Ciò avvia l'esecuzione dei messaggi nella coda dei messaggi di Loopers. Il thread principale è ora avviato e può iniziare a elaborare le attività dalla coda.
Si noti che alla riga 18, se l'esecuzione del codice va oltre Looper.loop() alla riga 17 e l'applicazione esce dal ciclo, verrà generata una RuntimeException. Ciò suggerisce che il metodo loop() idealmente non termina mai prematuramente. Vedremo come questo è nella prossima sezione.

3. Loop infinito () in Looper (processo 7,8,9)

/**
* AOSP
* Looper class
*/
public static void loop() {
	final Looper me = myLooper();
	if (me == null) {
		throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
	}
	final MessageQueue queue = me.mQueue;
	//code removed
	for (;;) {
		Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }            
    }
}
Figura 3: codice all'interno del metodo loop() nella classe Looper
Come possiamo vedere nel codice, c'è una coda di messaggi (riga 10) nel metodo Looper.loop() e queue.next() viene chiamato all'interno del ciclo. La MessageQueue è riempita con l'Handler di cui abbiamo parlato nella sezione precedente (vedi Processo 8). Presta attenzione all'interessante descrizione della condizione nel ciclo for: non ci sono argomenti, solo due punti e virgola dicono che si tratta di un ciclo infinito. Pertanto, Looper idealmente non termina mai a meno che il messaggio fornito non sia null.
Quindi ora abbiamo definito il thread principale da eseguire grazie al Looper, abbiamo anche visto che l'Handler aggiunge messaggi al Looper.loops() ed elabora i messaggi. Vediamo come chiamano insieme la nostra Attività.

4. Avvio di MainActivity (Processi da 10 a 15)

È importante ricordare che questo ciclo infinito e la gestione dei messaggi sono stati eseguiti nel metodo main() della classe ActivityThread, perché è lì che sono stati chiamati (vedere le righe da 12 a 17 nel codice). Abbiamo passato in rassegna Looper, MessageQueues e Handlers per il contesto. Torniamo quindi alla classe ActivityThread, in particolare alla classe H interna di cui abbiamo parlato in precedenza, che funge da principale Handler del thread principale.
Quindi, abbiamo un Looper che passa messaggi al nostro Handler, vediamo come vengono elaborati questi messaggi. Questo viene fatto all'interno della classe H. Questa classe contiene il metodo handleMessage (Message msg). Ricorda che tutte le classi che ereditano da Handler devono sovrascrivere questo metodo.
/**
* Retrieved from AOSP
* H class embedded in the ActivityThread class
*/
private class H extends Handler {
    //Several Application State Identifiers ...
        public void handleMessage(Message msg) {
                //other code
                switch (msg.what) {
                    case LAUNCH_ACTIVITY: {
                        //create Activity records
                        handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        ...
                        //handle other cases e.g ResumeActivity, PauseActivity, BindService, UnbindService etc.
                    }
                }
    }
}
Figura 4: la classe interna privata H e il suo metodo handleMessage()
Come puoi vedere nel codice, sull'ottava riga c'è un'istruzione switch, che determina l'elaborazione di un messaggio in arrivo dal suo contenuto.
Uno dei casi prevede l'avvio di un'attività (riga 11), la cosa interessante è che questo metodo è progettato per gestire circa 50 casi, che vanno da riprendi, metti in pausa, avvia attività, collega servizi, gestisci ricevitori, fornendo avvisi lowMemory o trimMemory quando la memoria del dispositivo è piena, ecc.
Nel caso LAUNCH_ACTIVITY, viene chiamato il metodo handleLaunchActivity(), come mostrato nella riga 13, vedere Process11 nel diagramma. Questo metodo chiama quindi un altro metodo chiamato performLaunchActivity(), che restituisce un oggetto Activity (vedere Figura 5, riga 7).
/**
* Retrieved from AOSP.
* ActivityThread class
*/
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        //... initialize graphics,do some logging, call GC if need be, etc
        Activity a = performLaunchActivity(r, customIntent);
        //... handle how to resume an existing activity
}
Figura 5: il metodo handleLaunchActivity() in cui viene creata l'attività
Il metodo performLaunchActivity() aggiunge informazioni importanti all'attività come Strumentazione, Contesto, Componente e Intento; e imposta anche Application. Questo metodo chiama quindi Instrumentation.callActivityOnCreate() (Processo 13), che è il passaggio finale prima di chiamare il metodo onCreate() nell'attività (Processo 14-15, vedere la Figura 5 (Codice), righe 8-10).
/ **
 * @Retrieved from AOSP
 * Instrumentation class
 * /
public void callActivityOnCreate (Activity activity, Bundle icicle) {
    // Note that the Activity has already been created in prepareLaunchActivity ().
    // all you need to do is call onCreate ()
    prePerformCreate (activity); // preparing Activity
    activity.performCreate (icicle); // call onCreate ()
    postPerformCreate (activity);
}
Figura 6: La classe Instrumentation avvia finalmente l'attività
A questo punto, la tua attività è caricata con molte variabili e metodi utili che puoi utilizzare per creare la tua nuova fantastica app Android! Tutto questo grazie a ActivityThread, il lavoro intelligente di Handler e Looper e un'enorme classe Activity da 7.600 linee che ti consente di allegare frammenti, ottenere contesto e gestire facilmente le visualizzazioni e molto altro ancora.
Ecco come nasce la nostra attività!
L'articolo originale è qui .