Warm Start est un scénario de démarrage d'application Android où le processus de l'application existe déjà en mémoire (par exemple, après avoir été minimisé), mais l'Activity a été détruite par le système pour économiser les ressources. Application.onCreate a déjà été exécuté, les classes sont chargées, mais l'UI est recréée. Selon Google, 2024, Warm Start prend 200 à 800 ms et représente environ 40 % de tous les démarrages sur les appareils avec 4 Go de RAM.
Points clés
Warm Start est un état entre Cold Start et Hot Start : le processus de l'application existe en mémoire (parfois dans le cache d'arrière-plan Linux), mais l'Activity n'est pas active et sera recréée. Lorsque Android manque de RAM, il peut supprimer l'Activity de la pile, laissant le processus vivant. Lorsque l'utilisateur revient à l'application, un Warm Start se produit : une nouvelle instance d'Activity est créée, les méthodes de cycle de vie onCreate → onStart → onResume sont exécutées, mais Application.onCreate et le chargement des classes sont ignorés.
Android décide de supprimer l'Activity en fonction de la priorité du processus (rang d'importance). Une Activity en arrière-plan (niveau PROCESS_STATE_IMPORTANT_FOREGROUND ou PROCESS_STATE_TOP_SLEEPING) peut être détruite 5 à 30 minutes après la minimisation de l'application, selon la RAM disponible. Sur les appareils avec 3 Go de RAM, l'Activity peut être supprimée en 10 minutes ; sur les appareils avec 8 Go de RAM, après plusieurs heures. Important : pendant Warm Start, onSaveInstanceState est appelé avant la destruction de l'Activity, et le développeur peut sauvegarder l'état de l'UI.
L'utilisateur ne voit pas la différence entre Warm et Cold Start — il appuie simplement sur l'icône de l'application et attend. Cependant, pendant Warm Start, un écran blanc peut apparaître si l'application n'a pas défini de thème personnalisé pour la fenêtre de démarrage. Google recommande de définir un thème personnalisé dans le manifeste (Theme.AppCompat.Light ou Theme.Material3.DayNight) pour l'Activity de démarrage, afin d'éviter le scintillement de l'écran blanc/noir pendant Warm Start. Sur Android 12+, l'API SplashScreen masque également cet effet.
Comprendre la différence entre les trois types de démarrage est essentiel pour choisir la bonne stratégie de profilage et d'optimisation. Chaque type a sa propre durée, ses propres goulots d'étranglement et ses propres outils de mesure.
| Critère | Cold Start | Warm Start | Hot Start |
|---|---|---|---|
| Processus | Créé à partir de zéro | Existe en mémoire | Existe en mémoire |
| Application.onCreate | Exécuté | Non exécuté | Non exécuté |
| Activity | Créée à partir de zéro | Créée à partir de zéro | Restaurée depuis la pile |
| Temps | 1–5 secondes | 200–800 ms | < 200 ms |
| Activity.onCreate | Complet | Complet (avec restauration) | Ignoré |
En pratique, Warm Start représente 30 % à 60 % de tous les démarrages d'applications, selon les habitudes de l'utilisateur et la RAM de l'appareil. Les utilisateurs qui gardent de nombreuses applications ouvertes (multitâche) rencontrent Warm Start plus souvent. Pour les réseaux sociaux et les messageries, Warm Start est le scénario le plus courant car l'application est toujours en arrière-plan. Pour les applications bancaires, au contraire, Cold Start prédomine (nettoyage forcé du processus pour des raisons de sécurité).
Warm Start se compose de trois phases, chacune pouvant être mesurée et optimisée. Contrairement à Cold Start, il n'y a pas de phase de fork ni de chargement de classes, mais il y a une phase de restauration d'état qui peut être coûteuse.
Le système vérifie si l'application a un thème pour la fenêtre de démarrage. Si le thème n'est pas défini, un écran blanc (ou noir, selon le système) s'affiche. Si le thème est défini, le fond du thème est affiché. Cette phase prend 10–30 ms, mais elle est visuellement perceptible si le thème ne correspond pas à l'UI réelle de l'application. Utilisez Theme.Material3.DayNight avec un windowBackground personnalisé dont la couleur correspond au fond du premier écran — cela crée un effet de chargement instantané.
Le système appelle onCreate en passant le Bundle savedInstanceState qui a été sauvegardé dans onSaveInstanceState avant la destruction de l'Activity. Si l'application a correctement sauvegardé l'état (texte des champs, position de défilement, données ViewModel), la restauration se produit rapidement. Sinon, l'Activity commence à partir de zéro et l'utilisateur voit un chargeur pendant le chargement des données. Point clé : les objets ViewModel survivent à Warm Start seulement si le processus n'a pas été détruit — pendant Warm Start, le ViewModel reste en mémoire.
Après onCreate, onStart → onResume sont exécutés, et le système déclenche le premier dessin. TTFD (Time To First Draw) pour Warm Start doit être inférieur à 300 ms sur un appareil de milieu de gamme. Si le premier écran contient un RecyclerView complexe avec des Views lourdes ou charge des images depuis le réseau, TTFD peut dépasser le seuil. Utilisez Placeholder et Shimmer pour un chargement fluide du contenu après la première image.
Mesurer Warm Start est plus complexe que Cold Start car vous devez simuler l'état où le processus est vivant mais l'Activity est détruite. La commande ADB standard avec le drapeau -S ne fonctionne pas — elle tue le processus. Utilisez des approches différentes pour Warm Start.
D'abord, lancez l'application via adb shell monkey ou appuyez sur l'icône, puis minimisez-la (adb shell input keyevent 3 keyevent HOME). Attendez 5 à 10 secondes pour que le système puisse supprimer l'Activity, puis exécutez adb shell am start -W (sans -S). La commande retournera un temps de démarrage plus court que Cold Start. Pour la reproductibilité, utilisez un script : lancer → attendre → accueil → attendre → lancer.
# Simulation de Warm Start via ADB
$ adb shell am start -W \
com.example.app/.MainActivity
# Sortie (Warm Start) :
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms
La bibliothèque androidx.benchmark.macro prend en charge la mesure de Warm Start. Dans le test, définissez startupMode = StartupMode.WARM — la bibliothèque lancera l'application, la minimisera, attendra (délai configurable), puis mesurera le redémarrage. Macrobenchmark effectue 10 à 20 itérations et calcule les percentiles. Dans CI/CD, vous pouvez définir un seuil : si P50 Warm Start dépasse 600 ms, le test échoue. Cela permet de suivre les régressions à chaque commit.
Firebase distingue automatiquement Cold et Warm Start en fonction du temps depuis la dernière fermeture de l'application. Si l'application a été ouverte dans les 30 dernières minutes, Firebase classe le démarrage comme Warm. Dans la console Firebase, vous verrez des graphiques séparés pour chaque type de démarrage, vous permettant d'évaluer l'efficacité des optimisations. Par exemple, après avoir implémenté la préservation d'état dans ViewModel, vous pouvez voir une réduction de 30 % du temps de Warm Start.
L'optimisation de Warm Start se concentre sur deux domaines : accélérer Activity.onCreate et la restauration correcte de l'état. Puisque Application.onCreate et le chargement des classes sont déjà terminés, le principal goulot d'étranglement est le code UI du premier écran.
Si l'état sauvegardé (savedInstanceState) contient des données nécessitant une désérialisation (Bitmap, String, JSON), faites-le sur un thread d'arrière-plan. Au lieu de lire directement depuis Bundle dans onCreate, lancez une coroutine et affichez un écran shimmer. En pratique, la désérialisation de Bundle sur un appareil de milieu de gamme prend 20–100 ms — cela semble peu, mais pour Warm Start, c'est 10–50 % du temps total. Utilisez le Saved State Module de Jetpack, qui sauvegarde et restaure automatiquement l'état du ViewModel dans le Bundle ou la base de données.
L'inflation du layout XML est l'une des étapes les plus coûteuses de Warm Start. Si le premier écran utilise un CoordinatorLayout complexe avec AppBar, CollapsingToolbar, NestedScrollView plus trois RecyclerViews, le temps d'inflation peut atteindre 300 ms. Solutions : utilisez ConstraintLayout pour une hiérarchie plate, appliquez ViewStub pour les sections non visibles au démarrage (bottom sheet, dialog), activez l'inflation asynchrone pour les fragments lourds via AsyncLayoutInflater. Dans Jetpack Compose, l'inflation n'est pas nécessaire, mais la compilation de l'arbre Compose pendant Warm Start peut prendre un temps similaire.
Pendant Warm Start, les données que l'application a chargées dans la session précédente peuvent déjà être dans le cache : base de données Room, SharedPreferences, cache en mémoire dans ViewModel. Si votre premier écran affiche une liste du serveur, vérifiez le cache au démarrage et mettez à jour les données en arrière-plan. Utilisez la stratégie cache-then-network : d'abord afficher les données en cache (instantanément), puis mettre à jour depuis le serveur (asynchrone). Cela réduit le temps perçu de Warm Start à 100–200 ms.
// ViewModel avec mise en cache pour Warm Start
class FeedViewModel : ViewModel() {
private val cache = MutableStateFlow<List<Item>>(emptyList())
init {
// Cache d'abord, puis réseau
viewModelScope.launch {
cache.emit(db.getItems()) // Warm Start : données déjà en BD
cache.emit(api.fetchItems()) // Mise à jour en arrière-plan
}
}
}
La préservation correcte de l'état est le facteur clé qui distingue un bon Warm Start d'un mauvais. L'utilisateur s'attend à revenir à l'application et à voir exactement ce qu'il a laissé — y compris la position de défilement, le texte dans les champs et les onglets sélectionnés.
Le système appelle onSaveInstanceState lorsque l'Activity est détruite, mais AVANT que le processus ne soit tué. Seuls les types de données simples (String, Int, Parcelable, Serializable) sont sauvegardés dans le Bundle. Pour les données complexes, utilisez SavedStateHandle dans ViewModel — il sauvegarde et restaure automatiquement les champs pendant Warm Start. Contrairement à onSaveInstanceState, SavedStateHandle fonctionne même si le processus survit à Warm Start (ViewModel n'est pas détruit). Exemple : pour le texte dans EditText, utilisez SavedStateHandle.getLiveData(«text») — le texte sera automatiquement sauvegardé et restauré.
Si le processus n'a pas été tué pendant Warm Start, le ViewModel reste en mémoire et onCleared n'est pas appelé. Cela signifie que toutes les données chargées dans la session précédente sont instantanément disponibles. Cependant, si le processus a été tué (appareil en veille prolongée pendant plus de 30 minutes), le ViewModel est détruit et recréé avec SavedStateHandle. Pour un comportement correct du ViewModel pendant Warm Start, utilisez SavedStateHandle avec les champs qui doivent être restaurés dans tout scénario. Différence : ViewModel avec @HiltViewModel prend en charge SavedStateHandle automatiquement.
| Mécanisme | Processus vivant | Processus tué |
|---|---|---|
| ViewModel | Données en mémoire | Détruit, recréé |
| SavedStateHandle | Données en mémoire | Restauré depuis Bundle |
| onSaveInstanceState | Appelé lors de la suppression de l'Activity | Non appelé |
| Room DB | Cache disponible | Cache disponible (disque) |
L'un des problèmes les plus courants de Warm Start — perdre la position de défilement. L'utilisateur a défilé jusqu'au 50e élément, a minimisé l'application, est revenu — et voit le début de la liste. Solution : sauvegardez layoutManager.onSaveInstanceState (sauvegarde la position et le décalage du premier élément visible) et restaurez-le dans onRestoreInstanceState. Vous pouvez également sauvegarder la dernière position visible dans SharedPreferences avec une clé de date/heure pour restaurer rapidement la position pendant Warm Start.
// Sauvegarder la position de défilement RecyclerView
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(
"rv_state", binding.recyclerView
.layoutManager?.onSaveInstanceState()
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedInstanceState?.getParcelable<Parcelable>("rv_state")
?.let { binding.recyclerView.layoutManager?.onRestoreInstanceState(it) }
}
Deux exemples pratiques d'optimisation de Warm Start : utilisation de SavedStateHandle dans ViewModel et restauration asynchrone de données complexes après le démarrage.
SavedStateHandle sauvegarde automatiquement les champs dans le Bundle et les restaure pendant Warm Start. Le champ de profil utilisateur (String, JSON) sera restauré sans requêtes inutiles au serveur. Si le processus a été tué, SavedStateHandle charge le dernier état sauvegardé depuis le Bundle.
class ProfileViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val profile: StateFlow<Profile?>
get() = savedStateHandle
.getStateFlow("profile", null)
fun loadProfile(id: String) {
viewModelScope.launch {
savedStateHandle["profile"] =
api.getProfile(id)
}
}
}
// Warm Start : profile n'est pas null, UI sans chargeur
// Après chargement : profile se met à jour dans SavedStateHandle
Si le premier écran contient un layout complexe (carte, dégradé, plusieurs listes), utilisez AsyncLayoutInflater pour gonfler les éléments lourds en arrière-plan. Pendant le gonflage du layout, affichez un placeholder avec effet shimmer. Ceci est particulièrement important pour Warm Start, où chaque milliseconde compte. AsyncLayoutInflater s'exécute sur un thread d'arrière-plan et passe le View prêt à un callback sur le thread principal.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Layout placeholder pour rendu instantané
setContentView(R.layout.placeholder_shimmer)
// Chargement asynchrone du layout lourd
AsyncLayoutInflater(this).inflate(
R.layout.activity_main_complex,
findViewById(R.id.container)
) { view, resId, parent ->
parent?.removeAllViews()
parent?.addView(view)
}
}
}
Foire aux questions
Oui, si au moment de Warm Start le système décide de tuer le processus de l'application (par exemple, pour libérer de la mémoire pour une autre application), le démarrage devient Cold Start à partir de zéro. Cela se produit sur les appareils avec 2–3 Go de RAM lorsque plusieurs applications fonctionnent simultanément. En fait, Warm Start n'est garanti que pendant 10–20 minutes après la minimisation sur les appareils de milieu de gamme.
Oui, si le processus n'a pas été tué, le ViewModel reste en mémoire et onCleared n'est pas appelé. C'est un avantage clé de Warm Start : toutes les données chargées via des requêtes réseau, le cache dans ViewModel — tout est disponible instantanément. Si le processus a été tué, le ViewModel est recréé via ViewModelProvider.Factory ou @HiltViewModel, et SavedStateHandle restaure les champs sauvegardés.
Théoriquement, Warm Start est toujours plus rapide que Cold Start, mais en pratique il existe des scénarios où la différence est minime : si Application.onCreate était léger (50 ms) et Activity.onCreate est lourd (800 ms), alors Warm Start (800 ms) est presque égal à Cold Start (850 ms). Dans ce cas, vous devez optimiser non pas Application, mais Activity.onCreate — il devient le goulot d'étranglement pour Warm Start.
L'API SplashScreen sur Android 12+ affiche un splash système (icône sur fond coloré) immédiatement au démarrage — à la fois pour Cold et Warm Start. Pour Warm Start, le splash est affiché seulement 100–300 ms, après quoi il est remplacé par l'UI de l'application. SplashScreen n'accélère pas le démarrage lui-même, mais masque le temps de création de l'Activity, améliorant la perception.
Oui, car Warm Start se produit 2 à 3 fois plus souvent que Cold Start. Si Cold Start prend 1,2 seconde et Warm Start prend 600 ms, alors 40 % des démarrages (Warm) prennent encore 0,6 seconde, ce qui est perceptible. Optimiser Warm Start à 200–300 ms donne à l'utilisateur une sensation de retour instantané. Sur les appareils avec 6+ Go de RAM, Warm Start peut représenter jusqu'à 80 % de tous les démarrages, faisant de son optimisation une priorité.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi