O aplicație lentă este principalul motiv pentru care utilizatorii șterg programe. Miimi de secundă de întârziere la pornire sau la derularea unei liste reduc retenția cu zeci de procente. Performanța (performance) nu este doar viteza, ci și stabilitatea: absența ANR, a crashurilor și a scurgerilor de memorie. Acest articol acoperă toate aspectele performanței: de la gestionarea memoriei (GC, ARC) la profilarea cu instrumente. Mai multe în ghidul oficial Android Performance.
Puncte cheie
Performanța aplicației este direct legată de jank — o întârziere vizibilă între acțiunea utilizatorului și răspunsul interfeței. Principalele cauze: blocarea firului principal (operații grele pe firul UI), redesenări frecvente ale layout-ului (overdraw), scurgeri de memorie (GC frecvent), algoritmi neoptimali (O(n²) pe seturi mari de date). Frame Rate (FPS) — numărul de cadre pe secundă. Pentru o experiență confortabilă sunt necesari 60 FPS stabili (Android) sau 120 FPS (iPhone Pro, iPad Pro). VSync sincronizează randarea cu rata de reîmprospătare a ecranului.
Jank apare când randarea unui singur cadru depășește 16,6 ms (pentru 60 FPS) sau 8,3 ms (pentru 120 FPS). Profilarea GPU (Profile GPU Rendering pe Android, Core Animation pe iOS) arată care etape de randare consumă cel mai mult timp. Etape principale: Layout (aranjarea elementelor), Draw (desenarea), Display (transferul în bufferul de cadre). Cea mai frecventă problemă este inflația layout-ului în XML, mai ales când se utilizează ConstraintLayout imbricat complex.
Time-to-Interactive (TTI) — timpul necesar pentru ca aplicația să fie complet gata de interacțiune. TTI include Cold Start, încărcarea datelor și inițializarea bibliotecilor. Google recomandă TTI sub 5 secunde, Apple — sub 2 secunde pentru ecranele principale. Lazy Loading — tehnică de încărcare întârziată a conținutului și bibliotecilor, critică pentru îmbunătățirea TTI. La IT Sectr, folosim inițializarea leneșă implicit în toate proiectele.
ANR și Crash sunt principalii dușmani ai performanței aplicațiilor mobile. ANR (Application Not Responding) — fereastră de dialog pe Android care apare dacă firul principal este blocat mai mult de 5 secunde. Cauze: cereri de rețea sincrone pe firul UI, lucrul cu baza de date fără corutine, decodificarea bitmap-urilor mari fără downsampling, deadlock pe firul principal. Stiva de apeluri ANR este salvată în /data/anr/traces.txt și permite determinarea locației exacte a blocării.
Crash — terminarea neașteptată a aplicației. Pe Android — o Exception (Java/Kotlin) sau Signal (cod nativ). Pe iOS — NSException sau semnal (EXC_BAD_ACCESS — acces la memorie eliberată). Instrumente de raportare a crashurilor: Firebase Crashlytics, Sentry, BugSnag. Acestea colectează stacktrace, date despre dispozitiv și pași de reproducere. Stack Overflow — depășirea stivei de apeluri prin recursivitate infinită. OutOfMemoryError — când heap-ul este plin.
StrictMode — instrument Android pentru detectarea încălcărilor de securitate a firelor de execuție. Permite stabilirea de reguli: ThreadPolicy (interzice disc/rețea pe firul principal), VmPolicy (detectează scurgeri de Activity, SQLite, CloseGuard). StrictMode ar trebui activat doar în compilările de debug — în versiunea de lansare nu ar trebui să funcționeze. Pe iOS, echivalentul este Main Thread Checker (Xcode), care detectează automat apelurile UIKit care nu sunt pe firul principal.
O scurgere de memorie (Memory Leak) apare atunci când un obiect rămâne în memorie chiar dacă aplicația nu îl mai folosește. Aceasta reduce direct performanța aplicației. Pe Android, GC (Garbage Collection) nu poate colecta un obiect dacă există o referință puternică la el. Cauze tipice: referințe statice la Activity, callback/observatori neanulați, clase interne cu referință implicită la clasa externă, Handler cu mesaje necurățate. LeakCanary — bibliotecă pentru detectarea automată a scurgerilor.
ARC (Automatic Reference Counting) — model de gestionare a memoriei pe iOS. Fiecare obiect are un contor de referințe (retain count). Când contorul ajunge la zero, memoria este eliberată. Un Retain Cycle apare când două obiecte mențin referințe puternice unul către celălalt (A → B și B → A). ARC nu va zerouri contoarele niciodată. Soluție: referințe slabe (weak) sau fără proprietar (unowned). Weak se anulează automat (devine nil) la eliberarea obiectului. Unowned nu se anulează dar garantează că obiectul este viu.
GC (Garbage Collection) funcționează pe Android (Java/Kotlin). GC întrerupe periodic execuția (pauză Stop-the-World) pentru a găsi și elibera obiectele inaccesibile. Declanșarea GC: când heap-ul atinge un anumit procent de umplere. ARC funcționează pe iOS (Swift/Objective-C) și nu are pauze — contoarele sunt actualizate atomic la fiecare atribuire. ARC este mai previzibil, dar poate acumula operații retain/release excesive la frecvență ridicată de atribuire.
Referință slabă (Weak Reference) și referință puternică (Strong Reference) — tipul de referință determină dacă GC/ARC poate elibera obiectul. Strong Reference — obiectul nu va fi colectat cât timp există această referință. Weak Reference — GC/ARC poate colecta obiectul; referința slabă devine nil (în Swift/Java WeakReference). Unowned Reference (Swift) — nu se anulează la eliberare; accesarea ei după moartea obiectului provoacă un crash. Pe Android se folosește java.lang.ref.WeakReference pentru referințe slabe.
Exemplu de detectare a unei scurgeri pe Android cu 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")
}
}
Profilarea este procesul de măsurare a performanței aplicației: CPU, memorie, rețea, consum de energie. Fără profilare, optimizarea oarbă este inutilă — nu veți ști ce parte a codului este de fapt lentă.
| Instrument | Platformă | Măsoară | Când să utilizați |
|---|---|---|---|
| Instruments (Time Profiler) | iOS | CPU, apeluri de funcții, timp de execuție | Optimizarea algoritmilor, căutarea blocajelor |
| Instruments (Allocations) | iOS | Memorie, număr de obiecte, retain counts | Căutarea scurgerilor și consumului excesiv de memorie |
| Instruments (Leaks) | iOS | Retain cycles, scurgeri de memorie | Verificare regulată înainte de lansare |
| Android Profiler (CPU) | Android | Utilizare CPU, activitate fire, traces | Căutarea blocărilor firului principal |
| Android Profiler (Memory) | Android | Heap dump, urmărirea alocărilor | Căutarea scurgerilor, analiza obiectelor |
| Android Profiler (Network) | Android | Trafic, viteză, temporizări cereri | Optimizarea apelurilor de rețea |
| LeakCanary | Android | Detectarea automată a scurgerilor de memorie | În toate etapele de dezvoltare |
| StrictMode | Android | Disc/rețea pe firul principal, scurgeri | Compilare de debug |
| Traceview / Systrace | Android | Urmărirea metodelor, evenimente de sistem | Analiză aprofundată a latenței |
Instruments (Xcode) — cel mai puternic instrument pentru iOS. Time Profiler arată care funcții consumă cel mai mult CPU. Allocations urmărește crearea și eliberarea obiectelor. Leaks găsește automat retain cycles. Pași de profilare: (1) lansați Instruments; (2) selectați șablonul (Time Profiler pentru CPU); (3) executați scenariul problemtic; (4) analizați stiva de apeluri — coloana cea mai largă este funcția cea mai "fierbinte".
Android Profiler este integrat în Android Studio (View → Tool Windows → Profiler). CPU Profiler arată încărcarea fiecărui fir. Memory Profiler — heap dump și urmărirea alocărilor. Network Profiler — toate cererile HTTP cu temporizări. Energy Profiler — consum de energie: WakeLock, Location, Network. Pentru urmărire detaliată se folosește Systrace (Android 10+) sau Perfetto — urmărirea sistemului cu precizie de microsecundă.
Pornirea aplicației este unul dintre indicatorii cheie de performanță. Se împarte în trei tipuri: Cold Start — aplicația pornește de la zero: se creează procesul, Application.onCreate (Android) / AppDelegate.applicationDidFinishLaunching (iOS), încărcarea claselor, inițializarea bibliotecilor. Warm Start — procesul există, dar Activity/ViewController este distrus (de exemplu, la rotirea ecranului sau la revenirea din memorie). Hot Start — Activity/ViewController este în memorie, aplicația este pur și simplu afișată (comutare de la o altă aplicație).
Cold Start este cea mai importantă metrică. Pe Android include: (1) lansarea Activity — încărcare XML, inițializare View; (2) primul cadru — timpul până la prima randare. Google recomandă: lansare Activity < 200 ms, primul cadru < 500 ms, TTI < 5 secunde. Optimizarea Cold Start: reduceți Application.onCreate (corutine pentru inițializare leneșă), utilizați SplashScreen API (Android 12+), amânați inițializarea bibliotecilor (WorkManager, DI), eliminați ContentProviders inutile.
Pe iOS, Cold Start include: încărcarea binarului Mach-O, dyld (linker-ul dinamic), inițializarea runtime-ului Objective-C, delegatul aplicației, primul controler. Chrome Custom Tabs (Android) și Universal Links (iOS) — tehnologii pentru deschiderea rapidă a conținutului extern în aplicație fără un Cold Start complet. Se recomandă testarea Cold Start pe dispozitive reale de gamă medie.
Dimensiunea aplicației este un factor de performanță pentru instalare și actualizări. Afectează conversia: fiecare 10 MB reduce conversia cu 1%. Google Play recomandă dimensiunea APK sub 150 MB; App Store — sub 200 MB (rețele celulare — 100 MB). Principalele metode de optimizare: compresia imaginilor (WebP în loc de PNG economisește 25-35%), vectorizarea (VectorDrawable pe Android, SF Symbols pe iOS), eliminarea codului neutilizat (R8/ProGuard), eliminarea resurselor neutilizate (lint → unused resources).
App Bundle (Android) — format de publicare în care Google Play generează un APK optimizat pentru fiecare dispozitiv. App Bundle reduce dimensiunea descărcării cu 20-40%. Dynamic Delivery — module descărcate la cerere (on-demand feature modules). Pe iOS, echivalentul este On-Demand Resources (ODR): resurse descărcate după prima lansare (niveluri de joc, videoclipuri).
Lazy Loading — tehnică în care modulele și bibliotecile nu sunt încărcate la pornire, ci sunt încărcate după necesitate. Split APK (Android) și App Slicing (iOS) — împărțirea aplicației în sloturi de arhitectură: arm64-v8a, x86_64. Optimizarea dimensiunii aplicației — un proces continuu: analizați compoziția APK (Analyze APK în Android Studio), eliminați iconițele duplicate, utilizați SVG în loc de mai multe densități PNG. La IT Sectr, includem verificarea dimensiunii build-ului în CI/CD pentru fiecare MR.
Întrebări frecvente
ANR (Application Not Responding) — fereastră de dialog pe Android care apare dacă firul principal este blocat mai mult de 5 secunde. Pentru a evita ANR, mutați toate operațiile grele (rețea, bază de date, procesare fișiere) în fire de execuție de fundal. Echivalentul pe iOS este frozen UI, când aplicația nu mai răspunde la atingeri.
O scurgere de memorie apare atunci când un obiect nu poate fi eliberat deoarece mai există referințe la el. Un Retain Cycle este o situație în iOS/Objective-C în care două obiecte se referă unul la altul (A → B → A) și ARC nu poate elibera niciunul. Soluție: referințe weak/unowned și curățarea la timp a callback-urilor.
Pentru iOS: Instruments (Time Profiler, Allocations, Leaks). Pentru Android: Android Profiler (CPU, Memory, Network), LeakCanary (scurgeri de memorie), StrictMode (încălcări ale firelor). Se recomandă combinarea profilării în timpul dezvoltării și integrării.
Cold Start — aplicația pornește de la zero: se creează procesul, se încarcă clasele, se execută Application.onCreate. Warm Start — procesul există, dar Activity/ViewController este recreat. Hot Start — Activity/ViewController este deja în memorie, pur și simplu afișat. Cold Start este cel mai lent (1-5 secunde) și este critic pentru experiența utilizatorului.
Metode principale: eliminați resursele și codul neutilizat (utilizați R8/ProGuard), vectorizați imaginile (VectorDrawable, SF Symbols), comprimați PNG/WebP (Android), utilizați App Bundle în loc de APK, eliminați bibliotecile inutile, utilizați Lazy Loading pentru module. Optimizarea dimensiunii poate reduce APK-ul cu 40-60%.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.