Aller au contenu
Tous les articles

Baseline Profiles sur Android : ce qui change vraiment votre temps de démarrage à froid en 2026

Un guide pratique des Baseline Profiles Android — les générer avec Macrobenchmark, les brancher dans Gradle, mesurer le vrai gain, et les trois autres leviers du démarrage à froid.

MFKAPPS 5 min de lecture

Le démarrage à froid est le chiffre de performance que chaque utilisateur ressent et que presque personne ne mesure. On appuie sur l’icône, il y a un instant d’écran vide ou d’écran de démarrage étiré, puis l’application apparaît. En dessous de 500 ms, personne ne remarque. Au-delà d’une seconde, ça se lit comme « lent » même si chaque écran suivant est instantané — les premières impressions collent, et l’icône de l’application est la première.

La majeure partie de ce chiffre n’a rien à voir avec votre mise en page ou votre requête de base de données. C’est le compilateur JIT qui effectue un travail froid, interprété, sur les chemins de code les plus sollicités de votre application, sans profil pour l’optimiser. Les Baseline Profiles existent pour sauter cette phase d’échauffement, et c’est l’un des rares outils de performance Android qui coûte un après-midi une fois et continue de rapporter à chaque nouvelle installation.

Ce qu’est réellement un Baseline Profile

Un Baseline Profile est un fichier texte — baseline-prof.txt — qui liste les classes et méthodes que votre application touche pendant ses parcours les plus courants : le démarrage à froid, et tout autre parcours que vous choisissez de profiler. Vous l’expédiez dans l’APK/AAB, et l’Android Runtime (ART) l’utilise pour précompiler ces méthodes en amont plutôt que de les interpréter ou de les compiler en JIT à la première utilisation.

L’effet est étroit mais réel : ça ne rend pas votre code plus rapide, ça supprime la taxe d’interprétation sur le code spécifique qui s’exécute pendant le démarrage. Sur Play, Google rapporte que cela réduit couramment le temps de démarrage à froid de 20 à 30 % pour les applications qui n’en avaient pas encore — votre gain dépend entièrement de la part de votre parcours de démarrage réellement couverte.

Générer un profil avec Macrobenchmark

Le profil doit provenir d’une exécution réelle, instrumentée — on ne peut pas l’écrire à la main de façon utile. La bibliothèque androidx.benchmark.macro s’en charge :

// build.gradle.kts (module baselineprofile)
plugins {
    id("androidx.baselineprofile")
}

dependencies {
    baselineProfile(project(":app"))
}
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() = rule.collect(
        packageName = "com.mfk.hydrame",
        includeInStartupProfile = true,
    ) {
        pressHome()
        startActivityAndWait()
        // Exécutez les parcours qui méritent d'être précompilés — pas que le splash.
        device.findObject(By.text("Log a glass")).click()
        device.waitForIdle()
    }
}

Exécutez la tâche Gradle :app:generateBaselineProfile sur un appareil réel ou un émulateur raisonnablement rapide (la génération de profil nécessite une compilation instrumentée quasi-root, que le plugin gère). Elle enregistre chaque classe touchée pendant cette exécution scriptée et écrit baseline-prof.txt dans app/src/main/, où le plugin AGP baseline profile le récupère automatiquement pour les builds de release.

Le point que la plupart des gens sautent : scriptez plus que l’écran de démarrage. Si le vrai goulot d’étranglement est votre premier rendu de liste avec des données issues de Room, ou le premier appel de provider d’un widget, incluez cette interaction dans le bloc collect. Un profil qui ne couvre que onCreate() rate l’essentiel du gain réel.

Mesurer le gain honnêtement

Ne faites pas confiance à un chronomètre et à votre pouce. Utilisez StartupTimingMetric dans un test macrobenchmark séparé, exécutez-le sur un build sans le profil et un build avec, et comparez les médianes sur assez d’itérations pour que le bruit se moyenne dans les deux sens :

@Test
fun startupCompilationModes() {
    benchmarkRule.measureRepeated(
        packageName = "com.mfk.hydrame",
        metrics = listOf(StartupTimingMetric()),
        iterations = 10,
        startupMode = StartupMode.COLD,
        compilationMode = CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require),
    ) {
        pressHome()
        startActivityAndWait()
    }
}

Remplacez compilationMode par CompilationMode.None() pour l’exécution de comparaison de référence. Dix itérations est un plancher raisonnable — l’état thermique de l’appareil et les processus en arrière-plan ajoutent assez de bruit pour que trois exécutions vous mentent dans un sens ou dans l’autre.

Ce que les Baseline Profiles ne corrigent pas

Ils ne remplacent pas la réduction réelle du travail de démarrage. Si votre Application.onCreate() initialise trois SDK, ouvre la base de données avec empressement et gonfle un premier écran lourd, un profil rend ce travail plus rapide à interpréter — il ne fait pas qu’il y en ait moins. Les gains s’additionnent avec, pas à la place de :

  • Différer l’initialisation non critique. Tout ce qui n’est pas nécessaire pour la première frame — SDK d’analytics, planification WorkManager non essentielle, récupération de configuration distante — passe sur un dispatcher en arrière-plan une fois l’UI visible, pas dans onCreate().
  • Le mode R8 full dans les builds de release. Réduire et optimiser le bytecode signifie qu’il y en a moins pour qu’ART le parcoure au départ, profil ou non.
  • Des singletons paresseux plutôt qu’empressés. Une instance de base de données by lazy qui s’initialise au premier accès DAO ne vous coûte rien au démarrage si le premier écran n’en a pas encore besoin.

J’ai découvert ça en construisant le widget d’écran d’accueil de Hydrame : le démarrage à froid propre du widget (un contexte de processus séparé, sans Application chaude sur laquelle s’appuyer) était plus lent que celui de l’application, parce qu’aucun chemin de code du widget ne figurait dans le profil. Ajouter un scénario spécifique au widget au script Macrobenchmark — pas seulement l’activité principale de l’application — a comblé la majeure partie de cet écart.

Quand ça vaut l’après-midi

Si votre application est déjà rapide, un Baseline Profile est un plus agréable. Si le démarrage à froid est la seule métrique en jaune dans le tableau de bord vitals de Play Console, c’est généralement l’après-midi le plus rentable que vous puissiez passer : pas de changement d’architecture, pas de risque fonctionnel, juste un fichier généré et un plugin Gradle. Générez-le une fois par version depuis vos vrais chemins critiques, gardez le test Macrobenchmark en CI pour qu’une refonte ne dépasse pas silencieusement ce que le profil couvre, et traitez le chiffre de démarrage à froid comme n’importe quelle autre régression — quelque chose que vous surveillez, pas que vous corrigez une fois et oubliez.