Décryptage du Processus de Lancement d'une Activité Android : Une Revue du Code Système

Le mécanisme de démarrage d'une activité est un concept fondamental que tout ingénieur Android expérimenté doit maîtriser. Il constitue un point de connaissance fréquemment abordé lors des entretiens techniques de haut niveau, essentiel aussi bien pour le développement d'applications que pour celui du framework. Pour en saisir pleinement les subtilités, une exploration approfondie du code source est indispensable. Cet article se propose de démystifier ce processus en s'appuyant sur les sources du système Android 8.1.

Bien que le processus de démarrage d'une activité implique de nombreux détails complexes, cet exposé se concentrera sur les étapes clés pour faciliter la compréhension. Pour une meilleure assimilation, nous présenterons d'abord les conclusions essentielles avant de détailler le flux d'exécution. Il est fortement recommandé d'ouvrir le code source dans votre environnement de développement et de suivre le raisonnement pas à pas.

  1. Divers Scénarios de Lancement d'Activités

Plusieurs voies peuvent mener au lancement d'une activité :

  1. Cliquer sur une icône de raccourci dans le Lanceur (Launcher), ce qui initie généralement l'activité racine de l'application.
  2. Effectuer une transition depuis une autre application vers une activité spécifique, qui peut être l'activité racine ou une autre (par exemple, lancer un appel, ouvrir l'appareil photo, naviguer sur le web).
  3. Démarrer une activité à partir d'un autre composant au sein de la même application.

De plus, le lancement peut survenir dans deux contextes distincts :

  1. Le processus de l'application cible n'existe pas encore (l'application n'est pas en cours d'exécution).
  2. Le processus de l'application cible est déjà actif (l'application a été lancée précédemment).

Chacun de ces scénarios présente des variations dans le processus de démarrage, mais le cœur du flux reste cohérent, avec des étapes ajoutées ou omises selon le contexte. Cet article utilisera le scénario de loin le plus représentatif : le lancement d'une activité racine à partir d'une icône du Lanceur.

  1. Vue d'Ensemble du Démarrage d'une Activité Racine

Le processus de lancement d'une activité racine depuis le Lanceur implique l'interaction de quatre processus principaux :

  1. Le processus de l'application Lanceur.
  2. Le processus système SystemServer, qui héberge l'ActivityManagerService (AMS).
  3. Le processus système Zygote.
  4. Le processus de l'application cible, où l'activité racine sera exécutée.

Voici les grandes étapes de cette interaction :

  1. Le processus du Lanceur demande à l'AMS de créer l'activité racine. Le Lanceur, en réponse au clic sur une icône, invoque la méthode startActivity. Cette requête transite par plusieurs couches d'appels dans le processus du Lanceur, pour finalement aboutir à l'AMS dans le processus SystemServer via la communication inter-processus (IPC) basée sur Binder.
  2. L'AMS demande la création du processus de l'application cible. À la réception de la requête, l'AMS vérifie si le processus de l'application cible existe déjà. Si ce n'est pas le cas, comme dans notre scénario, l'AMS sollicite le processus Zygote pour la création de ce nouveau processus. Zygote écoute ces requêtes via un socket dédié.
  3. Le processus Zygote duplique (fork) le processus cible. Zygote répond à la demande de l'AMS en effectuant une opération de 'fork' pour générer le nouveau processus d'application. C'est à ce stade qu'un objet ActivityThread est instancié, souvent appelé le "thread principal" de l'application, et sa méthode main est exécutée.
  4. L'AMS orchestre le lancement de l'activité dans le processus d'application. Une fois le processus de l'application créé, l'AMS, toujours dans SystemServer, effectue des appels en cascade qui aboutissent à une nouvelle communication Binder. Cette fois, la tâche de démarrage de l'activité est déléguée au proxy local ApplicationThread au sein du processus de l'application. Le flux d'exécution se déplace alors vers le processus de l'application cible. L'AMS effectue également des vérifications préalables, telles que les permissions utilisateur, la présence de l'activité, ou son mode de lancement.
  5. Le processus d'application crée et lance l'activité racine. Dans cette phase, les instances de l'activité et de l'application sont créées, les méthodes de cycle de vie appropriées sont invoquées, et le DecorView (qui contient la hiérarchie de vues de l'activité) est attaché à la fenêtre pour affichage.

Ces étapes s'appuient sur des concepts du démarrage système Android, tels que Zygote, SystemServer, l'AMS et le PackageManagerService (PMS). Pour une meilleure compréhension, il peut être utile de se familiariser avec le démarrage du système Android en général.

  1. Du Lanceur à l'ActivityManagerService (AMS)

Lorsque l'utilisateur clique sur une icône de raccourci dans le Lanceur, cela déclenche la méthode startActivity correspondant à l'activité ciblée. Le cheminement du code dans le processus du Lanceur est le suivant :

3.1. Démarrage depuis le Lanceur

// Fichier : com/android/launcher3/Launcher.java
public class LanceurApplication extends Activity {

    // Méthode appelée lors du clic sur un raccourci
    private void lancerRaccourciOuInfosApp(View elementVue) {
         // ... autres traitements et vérifications ...
         boolean operationReussie = demarrerActiviteDeManierSecurisee(elementVue, intentionLancement, elementInfo);
         // ... gestion des erreurs ou du succès ...
    }

    // Méthode intermédiaire pour un démarrage sécurisé d'activité
    public boolean demarrerActiviteDeManierSecurisee(View sourceVue, Intent intentionDemarrage, ItemInfo informationsElement) {
         // ... préparations du bundle d'options de lancement ...
         startActivity(intentionDemarrage, optionsDeBundle); // Appel à la méthode startActivity de la classe Activity
         // ... traitement post-lancement ...
         return true; // Simplifié
    }
}

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {

    /**
     * Lance une nouvelle activité. Cette méthode est la porte d'entrée générale pour le lancement.
     * @param intentionFinale L'Intent décrivant l'activité à lancer.
     * @param parametresLancement Options supplémentaires pour le lancement.
     */
    @Override
    public void startActivity(Intent intentionFinale, @Nullable Bundle parametresLancement) {
        if (parametresLancement != null) {
            startActivityForResult(intentionFinale, -1, parametresLancement);
        } else {
            startActivityForResult(intentionFinale, -1);
        }
    }

    /**
     * Lance une activité et attend un résultat. C'est ici que l'appel est délégué à Instrumentation.
     */
    public void startActivityForResult(String cleSource, Intent intentionOriginale, int codeRequete, @Nullable Bundle options) {
       if (mParentActivity == null) { // Indique que cette activité n'a pas de parent, c'est une activité racine
                // ... préparation des arguments pour Instrumentation ...
                Instrumentation.ActivityResult resultatInstrumention =
                    mInstrumentation.execStartActivity(
                        this, mMainThread.getApplicationThread(), mToken, this,
                        intentionOriginale, codeRequete, options);
                // ... traitement du résultat de l'instrumentation ...
            } else {
                // ... gestion pour les activités enfants ...
            }
    }
}

// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ... autres méthodes et membres ...

    /**
     * Exécute le démarrage effectif de l'activité en interfaçant avec le système.
     * Cette méthode effectue l'appel Binder vers l'ActivityManagerService.
     * @return Un code de résultat indiquant le succès ou l'échec de l'opération.
     */
    public ActivityResult execStartActivity(
            Context appelantContexte, IBinder threadAppBinder, IBinder jetonActivite,
            Activity activiteCible, Intent intentionLancement, int codeRequete,
            @Nullable Bundle optionsLancement) {
           // ... logiques internes de préparation ...
           int codeRetourAMS = ActivityManager.recupererService() // Obtient le proxy de l'AMS
                           .startActivity(threadAppBinder, activiteCible.getBasePackageName(), intentionLancement,
                                   intentionLancement.resolveTypeIfNeeded(activiteCible.getContentResolver()),
                                   jetonActivite, activiteCible != null ? activiteCible.mEmbeddedID : null,
                                   codeRequete, 0, null, optionsLancement); // Appel IPC vers l'AMS
           // ... analyse du code de retour ...
           return new ActivityResult(codeRetourAMS, null); // Retourne un objet résultat simplifié
    }
}

3.2. Récupération du Proxy AMS

L'instruction ActivityManager.recupererService() est cruciale. Elle permet d'obtenir un proxy distant de l'AMS dans le processus du Lanceur, facilitant ainsi la communication IPC. Ce modèle de singleton est courant dans le code source du système.

// Fichier : android/app/ActivityManager.java
public class ActivityManager {
    // ... autres méthodes et constantes ...

    /**
     * Récupère le service ActivityManager via son interface AIDL.
     * Cette méthode assure l'obtention d'une instance unique (singleton).
     * @return L'interface IActivityManager pour interagir avec le service.
     */
    public static IActivityManager recupererService() {
        return GestionnaireSingletonActivite.obtenirInstance();
    }

    private static final SingletonGenerique<iactivitymanager> GestionnaireSingletonActivite =
            new SingletonGenerique<iactivitymanager>() {
                @Override
                protected IActivityManager creerInstance() {
                    // Récupération du binder pour le service d'activité via ServiceManager
                    final IBinder binderService = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    // Conversion du binder en interface IActivityManager (proxy côté client)
                    final IActivityManager gestionnaireActivite = IActivityManager.Stub.asInterface(binderService);
                    return gestionnaireActivite;
                }
            };
}
</iactivitymanager></iactivitymanager>
// Fichier : android/util/Singleton.java
/**
 * Classe utilitaire abstraite pour l'initialisation paresseuse de singletons.
 * Permet de s'assurer qu'une seule instance d'un objet est créée et réutilisée.
 * @param <t> Le type de l'instance gérée par le singleton.
 */
public abstract class SingletonGenerique<t> {
    private T instanceUnique; // L'instance du singleton

    /**
     * Méthode abstraite à implémenter par les sous-classes pour créer l'instance.
     * Cette méthode sera appelée une seule fois, lors du premier accès.
     * @return L'instance nouvellement créée.
     */
    protected abstract T creerInstance();

    /**
     * Retourne l'instance unique de l'objet. Si elle n'existe pas, la crée.
     * La synchronisation assure la sécurité des threads.
     * @return L'instance unique de T.
     */
    public final T obtenirInstance() {
        synchronized (this) { // Verrouille l'objet courant pour la sécurité des threads
            if (instanceUnique == null) {
                instanceUnique = creerInstance(); // Crée l'instance si elle n'existe pas
            }
            return instanceUnique;
        }
    }
}
</t></t>

Après l'appel à startActivity via le proxy, le flux d'exécution se déplace du processus du Lanceur vers le processus SystemServer, où réside l'AMS.

  1. De l'ActivityManagerService (AMS) au Processus de l'Application

Cette phase implique un enchaînement d'appels dans l'AMS pour finalement déléguer le travail au processus de l'application cible. La complexité réside dans les nombreuses vérifications et préparations effectuées par l'AMS avant de confier la tâche.

// Fichier : com/android/server/am/ActivityStackSupervisor.java (simplifié)
public class ActivityStackSupervisor {
    // ... autres méthodes et membres ...

    /**
     * Méthode interne finale pour initier le véritable démarrage d'une activité.
     * C'est le point où le système demande au processus de l'application de lancer l'activité.
     * @param processRecord Le record du processus cible de l'application.
     * @param activityInfo Les informations de l'activité à lancer.
     * @param intentionDemarrage L'intention de lancement.
     * @return Vrai si le démarrage a été initié avec succès, faux sinon.
     */
    final boolean demarrerActiviteReellementVerrouille(ProcessRecord processRecord, ActivityInfo activityInfo, Intent intentionDemarrage) {
          // ... logiques diverses, vérifications de sécurité, gestion des piles d'activités, etc. ...
          // Appel via Binder au processus de l'application cible pour planifier le lancement
          processRecord.thread.planifierLancementActivite(new ActivityClientRecord(...)); // <-- Appel IPC vers l'ApplicationThread
          // ... suite du traitement côté AMS ...
          return true; // Exemple simplifié
    }
}

L'objet processRecord.thread est en réalité le proxy de ApplicationThread (IApplicationThread.Stub) dans le processus SystemServer. C'est à travers ce proxy que l'AMS peut invoquer des méthodes dans le processus de l'application cible. Ce mécanisme Binder est essentiel pour l'interaction entre les processus du système et ceux des applications.

Chaque application Android s'exécute dans un processus isolé. Les ressources (mémoire, etc.) ne sont pas partagées directement. Binder agit comme un pont, permettant à l'AMS (dans SystemServer) de communiquer avec l'ApplicationThread (dans le processus de l'application), et inversement pour les applications qui interagissent avec les services système comme l'AMS ou le WindowManagerService (WMS) via leurs proxies.

À partir de cet appel, le flux d'exécution entre dans le processus de l'application cible. Étant donné la complexité et la nature interne des appels au sein de l'AMS, nous ne détaillerons pas le code de cette partie, l'essentiel étant de comprendre que le contrôle est transféré au processus de l'application via IPC.

  1. Création et Démarrage de l'Activité Racine dans le Processus de l'Application

Le processus de l'application est maintenant responsable de la création et du lancement de l'activité. Ce travail est principalement géré par la classe ActivityThread, souvent appelée le "thread principal" de l'application. ActivityThread contient deux classes internes importantes : ApplicationThread (déjà mentionnée) et GestionnaireMessages (similaire à H dans le code source d'origine), qui est une sous-classe de Handler. GestionnaireMessages utilise le looper du thread principal pour exécuter sa méthode handleMessage sur ce même thread, assurant ainsi le basculement vers le thread UI pour les opérations critiques.

5.1. Transition vers le Thread Principal (UI Thread)

// Fichier : android/app/ActivityThread.java
public final class ActivityThread {
    // ... autres membres et méthodes ...

    // Handler principal d'ActivityThread, responsable de l'exécution sur le thread UI.
    final GestionnaireMessages mHandlerPrincipal = new GestionnaireMessages();

    /**
     * Implémentation de IApplicationThread qui reçoit les requêtes de l'AMS.
     * Cette classe agit comme un pont IPC vers le processus de l'application.
     */
    private class FilApplication extends IApplicationThread.Stub {
        // ... constructeur et autres méthodes ...

        /**
         * Reçoit du système la demande de planifier le lancement d'une nouvelle activité.
         * Les détails de l'activité sont encapsulés dans activityRecord.
         * @param activityRecord L'enregistrement client de l'activité à lancer.
         */
        public final void planifierLancementActivite(ActivityClientRecord activityRecord) {
            // Création d'un message pour le Handler principal (UI thread)
            Message messageLancement = Message.obtain();
            messageLancement.what = GestionnaireMessages.CODE_LANCEMENT_ACTIVITE;
            messageLancement.obj = activityRecord;
            // Envoi du message au handler principal (mHandlerPrincipal)
            mHandlerPrincipal.sendMessage(messageLancement);
        }
    }

    /**
     * Classe interne représentant le Handler principal pour le traitement des messages
     * sur le thread de l'interface utilisateur.
     */
    private class GestionnaireMessages extends Handler {
        // Codes de message pour le Handler
        public static final int CODE_LANCEMENT_ACTIVITE = 100; // Code pour le lancement d'activité

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case CODE_LANCEMENT_ACTIVITE: {
                    ActivityClientRecord recordActivite = (ActivityClientRecord) msg.obj;
                    // Appel de la méthode qui gère le lancement effectif de l'activité
                    gererLancementActivite(recordActivite, null, "LANCEMENT_ACTIVITE");
                    break;
                }
                // ... autres cas de messages ...
            }
        }
    }
}

Le message CODE_LANCEMENT_ACTIVITE est posté par ApplicationThread au GestionnaireMessages, qui l'exécute sur le thread principal de l'application en appelant gererLancementActivite.

5.2. Gestion du Lancement de l'Activité (handleLaunchActivity)

Cette méthode orchestre les étapes cruciales de la vie de l'activité, de son instanciation à son affichage.

// Fichier : android/app/ActivityThread.java
public final class ActivityThread {
    // ... autres méthodes ...

    /**
     * Gère le lancement d'une activité sur le thread principal de l'application.
     * Cette méthode orchestre la création et l'initialisation de l'activité.
     * @param clientRecord L'enregistrement contenant les informations de l'activité.
     * @param customIntent Une intention personnalisée si applicable.
     * @param debugReason Une chaîne pour le débogage.
     */
    private void gererLancementActivite(ActivityClientRecord clientRecord, Intent customIntent, String debugReason) {
         // ... vérifications initiales et configuration ...

         // Initialise le proxy pour le service WindowManager (WMS).
         // Ce service est nécessaire pour que l'activité puisse afficher son interface.
         GestionnaireFenêtreGlobal.initialiser(); // <-- Point 1 : Initialisation du WMS

         // Exécute les étapes pour instancier et configurer l'activité, puis appelle onCreate et onStart.
         Activity activiteLancee = executerLancementActivite(clientRecord, customIntent); // <-- Point 2 : Création de l'activité

         if (activiteLancee != null) {
             // Si l'activité a été créée avec succès, on passe à sa reprise (onResume)
             gererRepriseActivite(clientRecord.token, false, false, debugReason); // <-- Point 3 : Appel de la reprise (onResume)
         }
         // ... gestion des erreurs ou finalisation ...
    }
}

5.2.1. Initialisation du WindowManagerService (WMS)

Avant que l'activité ne puisse afficher son interface, un proxy vers le WindowManagerService est nécessaire. C'est le rôle de GestionnaireFenêtreGlobal.initialiser().

// Fichier : android/view/WindowManagerGlobal.java
public final class GestionnaireFenêtreGlobal {
    // ... autres membres et méthodes ...

    private static IWindowManager serviceWindowManagerCache; // Cache pour le proxy IWindowManager

    /**
     * Initialise les ressources globales liées au gestionnaire de fenêtres.
     * C'est le point d'entrée pour obtenir le proxy vers le service WindowManager (WMS).
     */
    public static void initialiser() {
        obtenirServiceWindowManager(); // Appel pour récupérer ou créer le proxy WMS
    }

    /**
     * Récupère le proxy d'interface AIDL pour le service WindowManager.
     * Utilise un mécanisme de singleton thread-safe pour garantir une seule instance.
     * @return Une instance de IWindowManager.
     */
    public static IWindowManager obtenirServiceWindowManager() {
        // Synchronisation pour la sécurité des threads lors de l'accès au cache
        synchronized (GestionnaireFenêtreGlobal.class) {
            if (serviceWindowManagerCache == null) {
                // Si le proxy n'est pas encore créé, on le récupère du ServiceManager
                serviceWindowManagerCache = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window")); // Nom du service "window"
            }
            return serviceWindowManagerCache; // Retourne le proxy existant ou nouvellement créé
        }
    }
}

5.2.2. Exécution du Lancement de l'Activité (performLaunchActivity)

Cette méthode est le cœur de la création de l'instance d'activité et de l'appel de ses méthodes de cycle de vie.

// Fichier : android/app/ActivityThread.java
public final class ActivityThread {
    // ... autres méthodes ...

    /**
     * Exécute les étapes de lancement d'une activité : instanciation, attachement,
     * et appels aux méthodes de cycle de vie (onCreate, onStart).
     * @param clientRecord L'enregistrement client de l'activité.
     * @param customIntent Une intention personnalisée (peut être null).
     * @return L'instance de l'activité créée, ou null en cas d'erreur.
     */
    private Activity executerLancementActivite(ActivityClientRecord clientRecord, Intent customIntent) {
        Activity activiteInstanciee = null;
        Context baseContexteApplication = clientRecord.packageInfo.createAppContext(this, clientRecord.token);

        // --- Étape A : Instanciation de l'activité ---
        try {
            ClassLoader chargeurClasses = baseContexteApplication.getClassLoader();
            activiteInstanciee = mInstrumentation.instancierNouvelleActivite(
                    chargeurClasses, clientRecord.component.getClassName(), clientRecord.intent);
            // ... autres vérifications ...
        } catch (Exception e) {
            throw new RuntimeException("Échec de l'instanciation de l'activité : " + clientRecord.component.flattenToShortString(), e);
        }

        // --- Étape B : Création/Récupération de l'instance Application ---
        Application applicationInstance;
        try {
            applicationInstance = clientRecord.packageInfo.preparerApplication(false, mInstrumentation);
        } catch (Exception e) {
            throw new RuntimeException("Échec de la préparation de l'application", e);
        }

        // --- Étape C : Attachement de l'activité et appel à onCreate ---
        try {
            activiteInstanciee.attacher(baseContexteApplication, this,
                    getInstrumentation(), clientRecord.token,
                    clientRecord.identifiant, applicationInstance, clientRecord.intent,
                    clientRecord.info, clientRecord.title, clientRecord.parent,
                    clientRecord.embeddedID, clientRecord.lastNonConfigurationInstances,
                    clientRecord.configuration, clientRecord.referrer, clientRecord.assistToken,
                    clientRecord.activityConfigCallback);

            if (clientRecord.isPersistable()) {
                mInstrumentation.appelerActiviteOnCreate(activiteInstanciee, clientRecord.state, clientRecord.persistentState);
            } else {
                mInstrumentation.appelerActiviteOnCreate(activiteInstanciee, clientRecord.state);
            }
        } catch (Exception e) {
            throw new RuntimeException("Échec de l'attachement ou de onCreate de l'activité : " + clientRecord.component.flattenToShortString(), e);
        }

        // --- Étape D : Appel à onStart ---
        if (!activiteInstanciee.isFinishing()) {
            activiteInstanciee.executerDemarrage();
        }

        return activiteInstanciee;
    }
}

5.2.2.1. Instanciation de l'Activité (Instrumentation.newActivity)
// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ...

    /**
     * Effectue l'instanciation de l'objet {@link Activity} d'un processus.
     * Cette méthode utilise le ClassLoader pour charger et instancier la classe d'activité.
     * @param classLoader Le ClassLoader à utiliser.
     * @param nomClasse L'activité fully qualified class name.
     * @param intentionLancement L'intention utilisée pour lancer l'activité.
     * @return L'objet Activity nouvellement instancié.
     */
    public Activity instancierNouvelleActivite(ClassLoader classLoader, String nomClasse,
            Intent intentionLancement)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // Charger la classe spécifiée et créer une nouvelle instance
        Class> classeActivite = classLoader.loadClass(nomClasse);
        return (Activity) classeActivite.newInstance();
    }
}

5.2.2.2. Création de l'Application (LoadedApk.makeApplication)

Ce processus crée ou récupère l'instance de la classe Application de l'application.

// Fichier : android/app/LoadedApk.java
public class LoadedApk {
    // ...

    /**
     * Prépare et retourne l'objet {@link Application} pour ce package.
     * Si l'application a déjà été créée, elle est retournée. Sinon, elle est instanciée et initialisée.
     * @param forceCreation Si vrai, force la création même si elle existe déjà.
     * @param instrumentation L'instance d'Instrumentation à utiliser.
     * @return L'objet Application.
     */
    public Application preparerApplication(boolean forceCreation, Instrumentation instrumentation) {
        if (mApplicationCache != null && !forceCreation) {
            return mApplicationCache; // Retourne l'instance existante si elle est en cache
        }

        Application applicationNouvelle = null;
        Context contexteApplicatif = null; // Simplifié pour l'exemple

        try {
            ClassLoader chargeurClassesApp = getClassLoader();
            String nomClasseApplication = (mApplicationInfo != null) ? mApplicationInfo.className : Application.class.getName();

            applicationNouvelle = mActivityThread.mInstrumentation.instancierNouvelleApplication(
                    chargeurClassesApp, nomClasseApplication, contexteApplicatif);
        } catch (Exception e) {
            throw new RuntimeException("Échec de la création de l'application", e);
        }

        mActivityThread.ajouterTouteApplication(applicationNouvelle);
        mApplicationCache = applicationNouvelle; // Met en cache l'instance créée

        if (instrumentation != null) {
            try {
                instrumentation.appelerApplicationOnCreate(applicationNouvelle); // <-- Appel à Application.onCreate()
            } catch (Exception e) {
                throw new RuntimeException("Erreur lors de l'appel de onCreate de l'application", e);
            }
        }
        return applicationNouvelle;
    }
}

// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ...

    /**
     * Effectue l'instanciation de l'objet {@link Application} d'un processus
     * en utilisant un ClassLoader et un nom de classe.
     * @param classLoader Le ClassLoader à utiliser.
     * @param nomClasseApp Le nom de la classe de l'Application.
     * @param contexte L'objet Context à attacher.
     * @return L'objet Application nouvellement instancié.
     */
    public Application instancierNouvelleApplication(ClassLoader classLoader, String nomClasseApp, Context contexte)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        Class> classeChargee = classLoader.loadClass(nomClasseApp);
        return instancierNouvelleApplication(classeChargee, contexte);
    }

    /**
     * Instancie l'objet {@link Application} du processus.
     * @param classeApp La classe de l'application à instancier.
     * @param contexte L'objet Context à attacher à l'application.
     * @return L'objet Application nouvellement instancié.
     */
    static public Application instancierNouvelleApplication(Class> classeApp, Context contexte)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        Application instanceApp = (Application) classeApp.newInstance();
        instanceApp.attacherBaseContexte(contexte); // Attache le contexte de base à l'application
        return instanceApp;
    }

    /**
     * Déclenche la méthode {@link Application#onCreate()} de l'application.
     * @param applicationCible L'instance de l'Application sur laquelle appeler onCreate.
     */
    public void appelerApplicationOnCreate(Application applicationCible) {
        applicationCible.onCreate();
    }
}

// Fichier : android/app/Application.java
public class Application extends ContextWrapper implements ComponentCallbacks2 {
    // ...

    /**
     * Méthode de cycle de vie appelée lors de la création de l'application.
     * Les développeurs peuvent la surcharger pour des initialisations globales.
     */
    @CallSuper
    public void onCreate() {
        // Implémentation par défaut.
    }
}

5.2.2.3. Appel de onCreate de l'Activité (Instrumentation.callActivityOnCreate)

Ceci déclenche la méthode onCreate() de votre activité racine, où vous configurez généralement votre interface utilisateur.

// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ...

    /**
     * Déclenche la méthode {@link Activity#onCreate(Bundle)} de l'activité.
     * @param activiteCible L'instance de l'Activité.
     * @param etatSauvegarde Le Bundle contenant l'état sauvegardé.
     */
    public void appelerActiviteOnCreate(Activity activiteCible, Bundle etatSauvegarde) {
        // preTraitementCreation(activiteCible); // Hooks de pré-création
        activiteCible.executerCreation(etatSauvegarde); // Appel interne de l'activité
        // postTraitementCreation(activiteCible); // Hooks de post-création
    }

    /**
     * Déclenche la méthode {@link Activity#onCreate(Bundle, PersistableBundle)} de l'activité.
     * @param activiteCible L'instance de l'Activité.
     * @param etatSauvegarde Le Bundle contenant l'état sauvegardé.
     * @param etatPersistant L'état persistant si l'activité est persistable.
     */
    public void appelerActiviteOnCreate(Activity activiteCible, Bundle etatSauvegarde,
            PersistableBundle etatPersistant) {
        // preTraitementCreation(activiteCible);
        activiteCible.executerCreation(etatSauvegarde, etatPersistant);
        // postTraitementCreation(activiteCible);
    }
}

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode interne finale pour gérer le processus de création de l'activité.
     * Elle délègue l'appel à la surcharge onCreate appropriée.
     * @param bundleEtat Le Bundle d'état à restaurer.
     */
    final void executerCreation(Bundle bundleEtat) {
        executerCreation(bundleEtat, null);
    }

    /**
     * Méthode interne finale pour gérer la création de l'activité,
     * supportant un état persistant.
     * @param bundleEtat Le Bundle d'état à restaurer.
     * @param etatPersistantBundle L'état persistant pour les activités configurées.
     */
    final void executerCreation(Bundle bundleEtat, PersistableBundle etatPersistantBundle) {
        // ... initialisations et configurations internes ...
        if (etatPersistantBundle != null) {
            onCreate(bundleEtat, etatPersistantBundle); // Appel à onCreate avec état persistant
        } else {
            onCreate(bundleEtat); // Appel à onCreate standard
        }
        // ... finalisation interne ...
    }

    /**
     * Méthode de cycle de vie appelée lors de la première création de l'activité.
     * @param bundleEtatSauvegarde L'état de l'activité précédemment sauvegardé, ou null.
     * @param etatPersistant L'état persistant, si l'activité est persistable.
     */
    public void onCreate(@Nullable Bundle bundleEtatSauvegarde,
            @Nullable PersistableBundle etatPersistant) {
        onCreate(bundleEtatSauvegarde); // Par défaut, appelle la version sans état persistant
    }

    /**
     * Méthode principale de cycle de vie appelée à la création de l'activité.
     * Annotation @MainThread assure l'exécution sur le thread UI.
     * @param bundleEtatSauvegarde L'état de l'activité précédemment sauvegardé, ou null.
     */
    @MainThread
    @CallSuper
    protected void onCreate(@Nullable Bundle bundleEtatSauvegarde) {
        // Les développeurs peuvent surcharger cette méthode pour initialiser l'UI et les données.
    }
}

5.2.2.4. Appel de onStart de l'Activité (Activity.performStart)

Après onCreate(), l'activité passe à l'état démarré.

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode interne finale qui déclenche la phase de démarrage de l'activité.
     * Elle est responsable d'appeler la méthode de cycle de vie {@link #onStart()}.
     */
    final void executerDemarrage() {
        // ... logiques internes et préparation ...
        mInstrumentation.appelerActiviteOnStart(this); // Délégue à Instrumentation pour le hook
        // ... finalisation du démarrage ...
    }
}

// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ...

    /**
     * Déclenche la méthode {@link Activity#onStart()} de l'activité.
     * @param activiteCible L'instance de l'Activité sur laquelle appeler onStart.
     */
    public void appelerActiviteOnStart(Activity activiteCible) {
        activiteCible.onStart();
    }
}

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode de cycle de vie appelée lorsque l'activité devient visible pour l'utilisateur.
     * C'est une bonne place pour enregistrer des récepteurs ou initialiser des ressources.
     */
    @CallSuper
    protected void onStart() {
        // Implémentation par défaut.
    }
}

5.3. Gestion de la Reprise d'Activité (handleResumeActivity)

Cette méthode gère la transition de l'activité vers l'état repris (résumé), ce qui inclut l'appel à onResume() et l'ajout final de la vue à la fenêtre.

// Fichier : android/app/ActivityThread.java
public final class ActivityThread {
    // ...

    /**
     * Gère la phase de reprise d'une activité sur le thread principal.
     * Cela inclut l'appel à onResume et l'ajout de la vue à la fenêtre.
     * @param jetonActivite Le jeton Binder de l'activité.
     * @param cacherSiVisible Si l'activité doit être cachée si elle était déjà visible.
     * @param demarrageForce Indique si un démarrage forcé est requis.
     * @param motifReprise Une chaîne décrivant le motif de la reprise.
     */
    final void gererRepriseActivite(IBinder jetonActivite, boolean cacherSiVisible, boolean demarrageForce, String motifReprise) {
          // ... logiques diverses, gestion d'état ...

          // Exécute les étapes pour la reprise de l'activité (appel à onResume)
          ActivityClientRecord recordActiviteCourant = executerRepriseActivite(jetonActivite, cacherSiVisible, motifReprise);

          // Si l'activité a bien été reprise, on procède à l'affichage de sa fenêtre.
          if (recordActiviteCourant != null) {
              // --- Processus d'ajout du DecorView à la fenêtre ---
              Window fenetreActivite = recordActiviteCourant.activity.getWindow(); // Récupère l'instance de PhoneWindow
              View vueDecor = fenetreActivite.getDecorView(); // Récupère l'instance du DecorView

              vueDecor.setVisibility(View.INVISIBLE); // Rendre la vue invisible avant l'ajout
              ViewManager gestionnaireVues = recordActiviteCourant.activity.getWindowManager(); // Le gestionnaire de vues
              WindowManager.LayoutParams parametresFenetre = fenetreActivite.getAttributes(); // Attributs de la fenêtre

              recordActiviteCourant.activity.mDecor = vueDecor; // Conserve une référence au DecorView dans l'Activity

              // ... vérifications et ajustements des paramètres de fenêtre ...

              // Ajoute le DecorView à la fenêtre via le WindowManager
              gestionnaireVues.addView(vueDecor, parametresFenetre); // <-- Opération d'affichage clé
              // ... post-traitement après l'ajout de la vue ...
          }
          // ... gestion des erreurs ou finalisation ...
    }
}

5.3.1. Exécution de la Reprise (performResumeActivity)
// Fichier : android/app/ActivityThread.java
public final class ActivityThread {
    // ...

    /**
     * Exécute les étapes nécessaires pour reprendre une activité spécifique.
     * Cela inclut l'appel à la méthode de cycle de vie onResume.
     * @param jetonActivite Le jeton Binder de l'activité à reprendre.
     * @param cacherSiVisible Si l'activité doit être cachée après la reprise.
     * @param motifReprise Une chaîne pour le débogage.
     * @return L'enregistrement client de l'activité si la reprise est réussie, sinon null.
     */
    public final ActivityClientRecord executerRepriseActivite(IBinder jetonActivite, boolean cacherSiVisible, String motifReprise) {
          ActivityClientRecord record = recupererActiviteParJeton(jetonActivite); // Récupère l'enregistrement de l'activité
          if (record != null) {
              // ... vérifications et mises à jour d'état ...
              record.activity.executerReprise(); // <-- Appel à la reprise interne de l'activité
              // ... autres traitements ...
          }
          return record;
    }
}

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode interne finale pour gérer le processus de reprise de l'activité.
     * Elle est responsable d'appeler les méthodes de cycle de vie onRestart (si applicable) et onResume.
     */
    final void executerReprise() {
         gererRedemarrage(); // Gère onRestart() si l'activité était arrêtée puis relancée
         mInstrumentation.appelerActiviteOnResume(this); // <-- Appel à onResume()
    }
}

5.3.1.1. Gestion du Redémarrage (performRestart)

Cette méthode est appelée si l'activité était précédemment à l'état "stopped" et est maintenant redémarrée. Dans le cas d'une nouvelle activité, ce chemin n'est pas emprunté.

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode interne finale pour gérer le redémarrage d'une activité.
     * Si l'activité était à l'état "stopped", elle sera redémarrée,
     * ce qui implique l'appel à onRestart() et ensuite onStart().
     */
    final void gererRedemarrage() {
         if (mEstArrete) { // mEstArrete est une variable d'état interne, similaire à mStopped
              mEstArrete = false; // Réinitialise l'état d'arrêt
              mInstrumentation.appelerActiviteOnRestart(this); // <-- Appel à onRestart
              // Après onRestart, une activité doit toujours passer par onStart
              executerDemarrage(); // Appel à onStart via la méthode interne
         }
    }
}

// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ...

    /**
     * Déclenche la méthode {@link Activity#onRestart()} de l'activité.
     * @param activiteCible L'instance de l'Activité sur laquelle appeler onRestart.
     */
    public void appelerActiviteOnRestart(Activity activiteCible) {
        activiteCible.onRestart();
    }
}

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode de cycle de vie appelée après que l'activité a été arrêtée,
     * puis est sur le point d'être redémarrée.
     * Elle sera suivie par onStart() et onResume().
     */
    @CallSuper
    protected void onRestart() {
        mAppelEffectue = true; // mAppelEffectue similaire à mCalled
        // Implémentation par défaut.
    }
}

5.3.1.2. Appel de onResume de l'Activité (Instrumentation.callActivityOnResume)

Finalement, la méthode onResume() de l'activité est invoquée.

// Fichier : android/app/Instrumentation.java
public class Instrumentation {
    // ...

    /**
     * Déclenche la méthode {@link Activity#onResume()} de l'activité.
     * @param activiteCible L'instance de l'Activité sur laquelle appeler onResume.
     */
    public void appelerActiviteOnResume(Activity activiteCible) {
        activiteCible.onResume(); // Appel de la méthode de cycle de vie onResume
    }
}

// Fichier : android/app/Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2 {
    // ...

    /**
     * Méthode de cycle de vie appelée lorsque l'activité a interagi avec l'utilisateur
     * et est devenue la première activité de la pile d'activités.
     * C'est le moment d'acquérir les ressources exclusives ou de démarrer des animations.
     */
    @CallSuper
    protected void onResume() {
        // Implémentation par défaut.
    }
}

5.4. Ajout du DecorView à la Fenêtre

La section du code gererRepriseActivite qui gère l'ajout du DecorView à la fenêtre est essentielle pour l'affichage de l'interface utilisateur. Le DecorView, racine de la hiérarchie de vues d'une activité (là où votre fichier de layout est gonflé via setContentView), est finalement attaché au Window via le WindowManager, ce qui déclenche le processus de rendu et de dessin. Ce processus est détaillé dans des articles sur le dessin des vues Android.

Le processus d'affichage est désormais complet pour l'activité racine. La compréhension de cette partie est grandement facilitée en la reliant aux méthodes de cycle de vie d'une activité Android.

  1. Autres Scénarios de Démarrage d'Activités

Ayant décortiqué le lancement d'une activité racine depuis le Lanceur, la compréhension d'autres scénarios devient plus simple. Par exemple :

  • Lancer une activité racine d'une application déjà en cours d'exécution : Le processus est similaire, mais l'étape de création du processus d'application par Zygote est omise, car le processus existe déjà.
  • Lancer une nouvelle activité au sein de la même application : Ce scénario n'implique que le processus de l'application et le processus SystemServer (pour l'AMS). Zygote n'est pas sollicité, et le processus de l'application est déjà actif.
  • Relancer une activité déjà existante (retour arrière, etc.) : Dans ce cas, l'activité ne passe pas par les étapes de création (onCreate) mais plutôt par un chemin de redémarrage (onRestart) suivi de onStart et onResume.

Ces variations illustrent comment le processus fondamental que nous avons étudié peut être adapté en ajoutant ou en sautant certaines étapes en fonction du contexte.

Étiquettes: Android Cycle de Vie Activité IPC Binder ActivityManagerService Zygote

Publié le 10 juin à 04h02