Le lag dans une application mobile est un retard perceptible entre l'action de l'utilisateur et la réaction de l'interface, causé par la surcharge du thread principal, des fuites mémoire ou des opérations d'E/S sous-optimales. Contrairement aux bugs liés à des erreurs logiques, le lag est un problème de performance : l'application fonctionne correctement mais lentement. Selon le AppDynamics Mobile App Performance Report 2024, 62 % des utilisateurs suppriment une application si elle lag plus de 3 secondes. Diagnostiquer le lag nécessite un profilage CPU, mémoire et réseau avec Android Studio Profiler et Xcode Instruments.
Points clés
Le lag dans une application mobile est un retard subjectivement perceptible entre l'action de l'utilisateur (toucher, balayage, saisie de texte) et la réponse de l'interface. Techniquement, le lag se mesure comme le temps entre l'événement d'entrée et le rendu complet de l'image : le seuil confortable est jusqu'à 100 ms, perceptible à partir de 200 ms, critique au-delà de 500 ms.
Dans la terminologie utilisateur, « lag » et « lenteur » sont souvent utilisés comme synonymes, mais techniquement le lag est un retard fixe (ex. 300 ms à chaque toucher), tandis que « lenteur » est un ralentissement intermittent : l'application fonctionne fluidement puis se fige pendant une seconde. Un bug, contrairement au lag, n'est pas lié à la vitesse mais à la correction de l'affichage.
Google Play et l'App Store prennent en compte les métriques de performance lors du classement des applications. Le taux d'ANR, la fréquence de jank et le temps de démarrage affectent la visibilité dans la recherche et la conversion d'installation. Une application avec un lag persistant perd jusqu'à 40 % des utilisateurs après le premier lancement.
Le lag se produit lorsque le thread principal d'UI ne parvient pas à traiter les images à 60 FPS (16,6 ms par image) ou 120 FPS (8,3 ms). Examinons les principales sources de ralentissements.
Toute opération synchrone dans le thread UI — lecture de SharedPreferences, travail avec une base de données via Room sans suspend, décodage d'une image en Bitmap — bloque le rendu de l'image. Sur Android, cela provoque du jank, sur iOS, un retard du rendu Core Animation.
Lorsque le ramasse-miettes sur Android ou ARC sur iOS libère de la mémoire, tous les threads sont suspendus. Des pauses GC fréquentes surviennent lors de la création de nombreux objets temporaires — par exemple, créer une nouvelle instance de ViewHolder à chaque appel d'adaptateur. Cela se manifeste par un défilement saccadé.
Des ConstraintLayout imbriqués, de multiples LinearLayout, des Views qui se chevauchent — chaque niveau d'imbrication augmente le temps de mesure et de layout pass. Xcode indique qu'une hiérarchie de couches profonde (plus de 10 niveaux) provoque une chute de FPS de 20 à 30 %.
Pour identifier les causes du lag, on utilise des profileurs intégrés aux IDE et des outils de surveillance système. Chaque outil résout sa propre tâche.
CPU Profiler montre quelles méthodes consomment du temps CPU et dans quels threads elles s'exécutent. Si une méthode de calcul lourd s'exécute dans le thread principal — c'est la cause racine. L'enregistrement d'une trace avec sample Java Method activé permet de voir la pile d'appels à tout moment et de trouver les points chauds.
L'outil équivalent pour iOS — Time Profiler — collecte des échantillons de pile chaque milliseconde et montre quel pourcentage de temps CPU chaque méthode consomme. Combiné avec le drapeau Main Thread Only, il filtre uniquement les opérations du thread principal, pointant directement vers les sources de lag.
Les requêtes réseau lentes créent l'impression de lag même si le thread UI n'est pas bloqué. Network Profiler dans Android Studio et Network Link Conditioner dans Xcode permettent de simuler des connexions lentes et d'identifier comment l'application se comporte dans des conditions réelles. Les réponses fragmentées sans progression et les gros payloads JSON sont des sources typiques de lag apparent.
Exemple de profilage d'une requête réseau avec OkHttp avec mesure de temps :
class TimingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val start = System.nanoTime()
val response = chain.proceed(chain.request())
val duration = (System.nanoTime() - start) / 1_000_000
Log.d("Timing", "Request took $duration ms")
return response
}
}
Résoudre le lag nécessite un travail systématique : de l'optimisation d'une seule méthode aux changements architecturaux. Examinons les techniques les plus efficaces.
Kotlin Coroutines avec Dispatchers.IO pour les requêtes réseau et Dispatchers.Default pour les calculs garantissent que le thread principal reste libre pour l'UI. Sur iOS, Grand Central Dispatch avec queue .global(qos: .userInitiated) pour les tâches d'arrière-plan et .main pour les mises à jour de l'UI est l'approche standard. Évitez les opérations sync entre les files d'attente.
RecyclerView sur Android et UICollectionView sur iOS nécessitent une configuration correcte : ViewHolder avec création minimale d'objets dans onBindViewHolder, DiffUtil pour le calcul des changements, prefetching pour le chargement anticipé des données. Sur iOS, utilisez diffable data source pour des mises à jour animées sans gestion manuelle.
Charger la même image à chaque défilement est un lag garanti. Coil (Android) et Kingfisher (iOS) mettent en cache les images en mémoire et sur disque, assurant un affichage instantané lors de requêtes répétées. Pour les données, utilisez Room avec une couche de cache basée sur Flow ou Combine.
Exemple de configuration du cache d'images avec Coil sur Android :
val imageLoader = ImageLoader(context) {
memoryCachePolicy(CachePolicy.ENABLED)
diskCachePolicy(CachePolicy.ENABLED)
crossfade(true)
size(512, 512)
}
// Loading with auto-caching enabled
imageView.load("https://example.com/image.jpg") {
placeholder(R.drawable.placeholder)
error(R.drawable.error)
}
Prévenir le lag est moins coûteux que de le corriger en production. Les mesures préventives sont intégrées au processus de développement au niveau des outils et de l'architecture.
StrictMode est un outil intégré d'Android qui détecte les opérations d'E/S accidentelles et les appels réseau sur le thread principal pendant le développement. Activez-le dans Application.onCreate avec une politique penaltyDeath pour les violations critiques. C'est le seul moyen de garantir que le développeur voit le problème avant le commit.
L'équivalent iOS — Main Thread Checker dans Xcode, faisant partie de Runtime Sanitization — vérifie automatiquement que tous les appels UIKit et AppKit s'exécutent sur le thread principal. Activez-le dans le schéma de build Debug et visez zéro avertissement dans le CI.
Ajoutez des exécutions de Macrobenchmark (Android) et XCTMetrics (iOS) à votre pipeline CI pour mesurer le temps de démarrage, le FPS de défilement et l'utilisation mémoire. Définissez des seuils : si un nouveau commit augmente le temps de démarrage de plus de 5 % — le build échoue.
Questions fréquentes
Le lag est une sensation subjective de retard qui peut survenir même avec un FPS élevé si le retard est causé par le temps de traitement de l'entrée, et non par le rendu. Un FPS faible (moins de 30 images/s) est une cause de lag, mais pas la seule.
Utilisez Frame Timing API sur Android (Choreographer) et CADisplayLink sur iOS pour mesurer le temps entre les images. Google Play Vitals montre le taux de jank dans des conditions réelles. Pour des mesures précises, utilisez Macrobenchmark avec des scénarios de défilement.
Les anciens appareils ont moins de cœurs CPU, moins de RAM et une mémoire plus lente. Une opération qui prend 5 ms sur un flagship peut prendre 50 ms sur un appareil d'entrée de gamme. Testez les performances sur des appareils bas de gamme et configurez Baseline Profiles pour la compilation AOT.
Oui, c'est l'une des méthodes les plus efficaces. Les images haute résolution consomment beaucoup de mémoire et de temps CPU pour le décodage. Utilisez le downscale à la taille de la View, les formats WebP (Android) et HEIC (iOS), et la mise en cache via Coil ou Kingfisher.
SwiftUI optimise automatiquement les mises à jour via le diffing, réduisant le risque de lag lors des changements de données. Cependant, les hiérarchies complexes et les reconstructions fréquentes du body peuvent provoquer des chutes de FPS. UIKit offre plus de contrôle sur les performances mais nécessite une optimisation manuelle.
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