Une application lente est la principale raison pour laquelle les utilisateurs suppriment des programmes. Des millisecondes de retard au démarrage ou lors du défilement d'une liste réduisent la rétention de dizaines de pour cent. La performance n'est pas seulement la vitesse, mais aussi la stabilité : absence d'ANR, de crashes et de fuites mémoire. Cet article couvre tous les aspects de la performance : de la gestion de la mémoire (GC, ARC) au profilage avec des outils. En savoir plus dans le guide officiel Android Performance.
Points clés
La performance de l'application est directement liée au jank — un retard notable entre l'action de l'utilisateur et la réponse de l'interface. Causes principales : blocage du thread principal (opérations lourdes sur le thread UI), redessins fréquents de la mise en page (overdraw), fuites mémoire (GC fréquent), algorithmes non optimaux (O(n²) sur de grands ensembles de données). Frame Rate (FPS) — nombre d'images par seconde. Pour une expérience confortable, il faut 60 FPS stables (Android) ou 120 FPS (iPhone Pro, iPad Pro). VSync synchronise le rendu avec la fréquence de rafraîchissement de l'écran.
Le jank se produit lorsque le rendu d'une seule image dépasse 16,6 ms (pour 60 FPS) ou 8,3 ms (pour 120 FPS). Le profilage GPU (Profile GPU Rendering sur Android, Core Animation sur iOS) montre quelles étapes du rendu prennent le plus de temps. Étapes principales : Layout (positionnement des éléments), Draw (dessin), Display (transfert au tampon d'images). Le problème le plus courant est l'inflation de la mise en page en XML, en particulier avec des ConstraintLayout imbriqués complexes.
Time-to-Interactive (TTI) — temps nécessaire à l'application pour être entièrement prête à interagir. Le TTI inclut le Cold Start, le chargement des données et l'initialisation des bibliothèques. Google recommande un TTI inférieur à 5 secondes, Apple — inférieur à 2 secondes pour les écrans principaux. Lazy Loading — technique de chargement différé du contenu et des bibliothèques, cruciale pour améliorer le TTI. Chez IT Sectr, nous utilisons l'initialisation paresseuse par défaut dans tous les projets.
ANR et Crash sont les principaux ennemis de la performance des applications mobiles. ANR (Application Not Responding) — boîte de dialogue sur Android qui apparaît si le thread principal est bloqué plus de 5 secondes. Causes : requêtes réseau synchrones sur le thread UI, travail avec la base de données sans coroutines, décodage de bitmap volumineux sans downsampling, deadlock sur le thread principal. La pile d'appels ANR est sauvegardée dans /data/anr/traces.txt et permet de déterminer l'emplacement exact du blocage.
Crash — arrêt inattendu de l'application. Sur Android — c'est une Exception (Java/Kotlin) ou un Signal (code natif). Sur iOS — NSException ou signal (EXC_BAD_ACCESS — accès à la mémoire libérée). Outils de Crash Reporting : Firebase Crashlytics, Sentry, BugSnag. Ils collectent la stacktrace, les données de l'appareil et les étapes de reproduction. Stack Overflow — débordement de la pile d'appels par récursion infinie. OutOfMemoryError — lorsque le tas (heap) est plein.
StrictMode — outil Android pour détecter les violations de sécurité des threads. Il permet de définir des règles : ThreadPolicy (interdire le disque/réseau sur le thread principal), VmPolicy (détecter les fuites d'Activity, SQLite, CloseGuard). StrictMode doit être activé uniquement dans les versions de débogage — en release, il ne doit pas fonctionner. Sur iOS, l'équivalent est Main Thread Checker (Xcode), qui détecte automatiquement les appels UIKit hors du thread principal.
Une fuite mémoire (Memory Leak) est une situation où un objet reste en mémoire alors que l'application ne l'utilise plus. Cela réduit directement les performances de l'application. Sur Android, le GC (Garbage Collection) ne peut pas collecter un objet s'il y a une référence forte vers lui. Causes typiques : références statiques à Activity, callbacks/observateurs non annulés, classes internes avec référence implicite à la classe externe, Handler avec des messages non nettoyés. LeakCanary — bibliothèque de détection automatique des fuites.
ARC (Automatic Reference Counting) — modèle de gestion de mémoire sur iOS. Chaque objet a un compteur de références (retain count). Lorsque le compteur atteint zéro, la mémoire est libérée. Un Retain Cycle se produit lorsque deux objets maintiennent des références fortes l'un vers l'autre (A → B et B → A). L'ARC ne mettra jamais les compteurs à zéro. Solution : références faibles (weak) ou sans propriétaire (unowned). Weak est automatiquement nullifiée (devient nil) lors de la libération de l'objet. Unowned n'est pas nullifiée mais garantit que l'objet est vivant.
GC (Garbage Collection) fonctionne sur Android (Java/Kotlin). Le GC interrompt périodiquement l'exécution (pause Stop-the-World) pour trouver et libérer les objets inatteignables. Déclencheur du GC : lorsque le tas (heap) atteint un certain pourcentage de remplissage. L'ARC fonctionne sur iOS (Swift/Objective-C) et n'a pas de pauses — les compteurs sont mis à jour atomiquement à chaque affectation. L'ARC est plus prévisible mais peut accumuler des opérations retain/release excessives en cas de fréquence d'affectation élevée.
Référence faible (Weak Reference) et référence forte (Strong Reference) — le type de référence détermine si le GC/ARC peut libérer l'objet. Strong Reference — l'objet ne sera pas collecté tant que cette référence existe. Weak Reference — le GC/ARC peut collecter l'objet ; la référence faible devient nil (en Swift/Java WeakReference). Unowned Reference (Swift) — n'est pas nullifiée lors de la libération ; y accéder après la mort de l'objet provoque un crash. Sur Android, java.lang.ref.WeakReference est utilisé pour les références faibles.
Exemple de détection de fuite sur Android avec LeakCanary :
// Утечка: анонимный класс держит ссылку на Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// Используем `this@MainActivity`, сохраняя ссылку на Activity
Log.d("TAG", "Handler received message")
}
}
handler.sendEmptyMessageDelayed(0, 60000)
}
}
// Исправление: статический Handler + WeakReference
class SafeHandler(activity: MainActivity) : Handler() {
private val weakActivity =
WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get() ?: return
Log.d("TAG", "Handler received message")
}
}
Le profilage est le processus de mesure des performances de l'application : CPU, mémoire, réseau, consommation d'énergie. Sans profilage, l'optimisation aveugle est inutile — vous ne saurez pas quelle partie du code ralentit réellement.
| Outil | Plateforme | Mesure | Quand utiliser |
|---|---|---|---|
| Instruments (Time Profiler) | iOS | CPU, appels de fonction, temps d'exécution | Optimisation d'algorithmes, recherche de goulots d'étranglement |
| Instruments (Allocations) | iOS | Mémoire, nombre d'objets, retain counts | Recherche de fuites et consommation excessive de mémoire |
| Instruments (Leaks) | iOS | Retain cycles, fuites mémoire | Vérification régulière avant la release |
| Android Profiler (CPU) | Android | Utilisation CPU, activité des threads, traces | Recherche de blocages du thread principal |
| Android Profiler (Memory) | Android | Heap dump, suivi des allocations | Recherche de fuites, analyse d'objets |
| Android Profiler (Network) | Android | Trafic, vitesse, temps de requête | Optimisation des appels réseau |
| LeakCanary | Android | Détection automatique des fuites mémoire | À toutes les étapes du développement |
| StrictMode | Android | Disque/réseau sur le thread principal, fuites | Build de débogage |
| Traceview / Systrace | Android | Traçage de méthodes, événements système | Analyse approfondie de la latence |
Instruments (Xcode) — l'outil le plus puissant pour iOS. Time Profiler montre quelles fonctions consomment le plus de CPU. Allocations suit la création et la libération d'objets. Leaks détecte automatiquement les retain cycles. Étapes du profilage : (1) lancer Instruments ; (2) sélectionner un modèle (Time Profiler pour CPU) ; (3) exécuter le scénario problématique ; (4) analyser la pile d'appels — la colonne la plus large est la fonction la plus « chaude ».
Android Profiler est intégré à Android Studio (View → Tool Windows → Profiler). CPU Profiler montre la charge de chaque thread. Memory Profiler — heap dump et suivi des allocations. Network Profiler — toutes les requêtes HTTP avec timings. Energy Profiler — consommation d'énergie : WakeLock, Location, Network. Pour un traçage détaillé, on utilise Systrace (Android 10+) ou Perfetto — traçage système avec une précision à la microseconde.
Le démarrage de l'application est l'un des indicateurs de performance clés. Il se divise en trois types : Cold Start — l'application démarre à partir de zéro : création du processus, Application.onCreate (Android) / AppDelegate.applicationDidFinishLaunching (iOS), chargement des classes, initialisation des bibliothèques. Warm Start — le processus existe, mais l'Activity/ViewController est détruit (par exemple, lors de la rotation de l'écran ou du retour de la mémoire). Hot Start — l'Activity/ViewController est en mémoire, l'application est simplement affichée (passage depuis une autre application).
Le Cold Start est la métrique la plus importante. Sur Android, il inclut : (1) launch Activity — chargement XML, initialisation de la View ; (2) première image — temps jusqu'au premier rendu. Google recommande : launch Activity < 200 ms, première image < 500 ms, TTI < 5 secondes. Optimisation du Cold Start : réduire Application.onCreate (coroutines pour l'initialisation paresseuse), utiliser SplashScreen API (Android 12+), différer l'initialisation des bibliothèques (WorkManager, DI), supprimer les ContentProviders inutiles.
Sur iOS, le Cold Start inclut : chargement du binaire Mach-O, dyld (éditeur de liens dynamique), initialisation du runtime Objective-C, délégué d'application, premier contrôleur. Chrome Custom Tabs (Android) et Universal Links (iOS) — technologies pour ouvrir rapidement du contenu externe dans l'application sans Cold Start complet. Il est recommandé de tester le Cold Start sur des appareils réels de milieu de gamme.
La taille de l'application est un facteur de performance pour l'installation et les mises à jour. Elle affecte la conversion : chaque 10 MB réduit la conversion de 1 %. Google Play recommande une taille d'APK inférieure à 150 MB ; l'App Store — inférieure à 200 MB (réseaux cellulaires — 100 MB). Principales méthodes d'optimisation : compression d'images (WebP au lieu de PNG économise 25-35 %), vectorisation (VectorDrawable sur Android, SF Symbols sur iOS), suppression du code inutilisé (R8/ProGuard), suppression des ressources inutilisées (lint → unused resources).
App Bundle (Android) — format de publication où Google Play génère un APK optimisé pour chaque appareil. App Bundle réduit la taille du téléchargement de 20 à 40 %. Dynamic Delivery — modules téléchargés à la demande (on-demand feature modules). Sur iOS, l'équivalent est On-Demand Resources (ODR) : ressources téléchargées après le premier lancement (niveaux de jeu, vidéos).
Lazy Loading — technique où les modules et bibliothèques ne sont pas chargés au démarrage mais chargés selon les besoins. Split APK (Android) et App Slicing (iOS) — division de l'application en segments d'architecture : arm64-v8a, x86_64. Optimisation de la taille de l'App — un processus continu : analysez la composition de l'APK (Analyze APK dans Android Studio), supprimez les icônes en double, utilisez SVG au lieu de plusieurs densités PNG. Chez IT Sectr, nous incluons la vérification de la taille du build dans le CI/CD pour chaque MR.
Questions fréquentes
ANR (Application Not Responding) — boîte de dialogue qui apparaît sur Android si le thread principal est bloqué plus de 5 secondes. Pour éviter l'ANR, déplacez toutes les opérations lourdes (réseau, base de données, traitement de fichiers) vers des threads en arrière-plan. L'équivalent sur iOS est frozen UI, lorsque l'application cesse de répondre aux touches.
Une fuite mémoire se produit lorsqu'un objet ne peut pas être libéré parce que des références vers lui existent encore. Un Retain Cycle est une situation en iOS/Objective-C où deux objets se référencent mutuellement (A → B → A) et l'ARC ne peut libérer aucun des deux. Solution : références weak/unowned et nettoyage opportun des callbacks.
Pour iOS : Instruments (Time Profiler, Allocations, Leaks). Pour Android : Android Profiler (CPU, Memory, Network), LeakCanary (fuites mémoire), StrictMode (violations de threads). Il est recommandé de combiner le profilage pendant le développement et l'intégration.
Cold Start — l'application démarre à partir de zéro : le processus est créé, les classes sont chargées, Application.onCreate s'exécute. Warm Start — le processus existe mais l'Activity/ViewController est recréée. Hot Start — l'Activity/ViewController est déjà en mémoire, simplement affichée. Cold Start est le plus lent (1-5 secondes) et est critique pour l'expérience utilisateur.
Méthodes principales : supprimer les ressources et le code inutilisés (utiliser R8/ProGuard), vectoriser les images (VectorDrawable, SF Symbols), compresser PNG/WebP (Android), utiliser App Bundle au lieu d'APK, supprimer les bibliothèques inutiles, utiliser Lazy Loading pour les modules. L'optimisation de la taille peut réduire l'APK de 40 à 60 %.
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.