Перформансе у мобилном развоју: шта је то, које метрике и како побољшати

Аутор: IT Sectr Објављено: 2026-03-25 Време читања: 12 мин

Спорa апликација је главни разлог због којег корисници бришу програме. Делићи секунде кашњења при покретању или померању листе смањују задржавање за десетине процената. Перформансе (performance) нису само брзина, већ и стабилност: одсуство ANR, крашeва и цурења меморије. Овај чланак покрива све аспекте перформанси: од управљања меморијом (GC, ARC) до профилисања алатима. Више у званичном водичу за Android Performance.

Главне тачке

  • ANR и Crash — главни непријатељи корисничког искуства; спречавају се позадинским нитима
  • Цурење меморије и Retain Cycle доводе до OOM крашева; решавају се слабим референцама и алатима
  • GC (Android) и ARC (iOS) — модели управљања меморијом; разумевање њиховог рада је кључно
  • Профилисање (Instruments, Android Profiler, LeakCanary) — обавезна фаза развоја
  • Cold Start — најважнија метрика покретања; оптимизација Application.onCreate и лења иницијализација
  • Величина апликације — користити App Bundle, R8, VectorDrawable и WebP за смањење величине

Зашто апликација успорава?

Перформансе апликације су директно повезане са jank-ом — приметним кашњењем између акције корисника и реакције интерфејса. Главни узроци: блокирање главне нити (тешке операције на UI нити), често прецртавање распореда (overdraw), цурење меморије (чести GC), неоптимални алгоритми (O(n²) на великим подацима). Frame Rate (FPS) — број кадрова у секунди. За удобно искуство потребно је стабилних 60 FPS (Android) или 120 FPS (iPhone Pro, iPad Pro). VSync синхронизује рендеровање са брзином освежавања екрана.

Jank настаје када рендеровање једног кадра премаши 16,6 ms (за 60 FPS) или 8,3 ms (за 120 FPS). Профилисање GPU (Profile GPU Rendering на Android-у, Core Animation на iOS-у) показује које фазе рендеровања заузимају највише времена. Главне фазе: Layout (распоред елемената), Draw (цртање), Display (пренос у бафер кадра). Најчешћи проблем је инфлација распореда у XML-у, посебно при коришћењу сложених угњеждених ConstraintLayout-а.

Time-to-Interactive (TTI) — време за које апликација постаје потпуно спремна за интеракцију. TTI укључује Cold Start, учитавање података и иницијализацију библиотека. Google препоручује TTI мање од 5 секунди, Apple — мање од 2 секунде за главне екране. Lazy Loading — техника одложеног учитавања садржаја и библиотека, кључна за побољшање TTI. У IT Sectr-у подразумевано користимо лењу иницијализацију на свим пројектима.

ANR и Crash

ANR и Crash — главни непријатељи перформанси мобилне апликације. ANR (Application Not Responding) — дијалошки оквир на Android-у који се појављује ако је главна нит блокирана више од 5 секунди. Узроци: синхрони мрежни захтеви на UI нити, рад са базом података без корутина, декодирање великог битмапа без downsampling-а, deadlock на главној нити. ANR стек позива се чува у /data/anr/traces.txt и омогућава одређивање тачне локације блокирања.

Crash — неочекивани завршетак апликације. На Android-у — Exception (Java/Kotlin) или Signal (изворни код). На iOS-у — NSException или сигнал (EXC_BAD_ACCESS — приступ ослобођеној меморији). Алати за извештавање о крашевима: Firebase Crashlytics, Sentry, BugSnag. Они прикупљају stacktrace, податке о уређају и кораке репродукције. Stack Overflow — прекорачење стека позива услед бесконачне рекурзије. OutOfMemoryError — када је хеап пун.

StrictMode — Android алат за откривање кршења безбедности нити. Омогућава постављање правила: ThreadPolicy (забрани диск/мрежу на главној нити), VmPolicy (откриј цурење Activity, SQLite, CloseGuard). StrictMode треба укључити само у debug верзији — у релизу не би требало да ради. На iOS-у аналог је Main Thread Checker (Xcode), који аутоматски открива UIKit позиве који нису на главној нити.

Управљање меморијом (GC, ARC, Retain Cycle)

Цурење меморије

Цурење меморије (Memory Leak) — ситуација када објекат остаје у меморији иако га апликација више не користи. То директно смањује перформансе апликације. На Android-у, GC (Garbage Collection) не може сакупити објекат ако постоји јака референца на њега. Типични узроци: статичке референце на Activity, непоништени callback-ови/посматрачи, унутрашње класе са имплицитном референцом на спољну класу, Handler са неочишћеним порукама. LeakCanary — библиотека за аутоматско откривање цурења.

Retain Cycle (Циклус задржавања)

ARC (Automatic Reference Counting) — модел управљања меморијом на iOS-у. Сваки објекат има бројач референци (retain count). Када бројач достигне нулу, меморија се ослобађа. Retain Cycle настаје када два објекта држе јаке референце један на другог (A → B и B → A). ARC никада неће нулирати бројаче. Решење: слабе (weak) или безвласничке (unowned) референце. Weak се аутоматски поставља на nil када се објекат ослободи. Unowned се не поставља на nil, али гарантује да је објекат жив.

GC vs ARC

GC (Garbage Collection) ради на Android-у (Java/Kotlin). GC периодично зауставља извршење (пауза Stop-the-World) да би пронашао и ослободио недостижне објекте. GC окидач: када се хеап напуни до одређеног процента. ARC ради на iOS-у (Swift/Objective-C) и нема паузе — бројачи се ажурирају атомски при сваком додељивању. ARC је предвидљивији, али може акумулирати прекомерне retain/release операције при високој учесталости додељивања.

Слаба референца (Weak Reference) и јака референца (Strong Reference) — тип референце одређује да ли GC/ARC може ослободити објекат. Strong Reference — објекат неће бити сакупљен док постоји ова референца. Weak Reference — GC/ARC може сакупти објекат; слаба референца постаје nil (у Swift/Java WeakReference). Unowned Reference (Swift) — не поставља се на nil при ослобађању; приступ њој након смрти објекта изазива краш. На Android-у се за слабе референце користи java.lang.ref.WeakReference.

Пример откривања цурења на Android-у путем LeakCanary-ја:

kotlin
// Утечка: анонимный класс держит ссылку на 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")
    }
}

Профилисање (Instruments, Android Profiler)

Профилисање — процес мерења перформанси апликације: CPU, меморија, мрежа, потрошња енергије. Без профилисања, оптимизација на слепо је бескорисна — нећете сазнати који део кода заправо успорава.

Алат Платформа Мери Када користити
Instruments (Time Profiler)iOSCPU, позиви функција, време извршењаОптимизација алгоритама, тражење уских грла
Instruments (Allocations)iOSМеморија, број објеката, retain countsТражење цурења и прекомерне потрошње меморије
Instruments (Leaks)iOSRetain cycles, цурење меморијеРедовна провера пре издавања
Android Profiler (CPU)AndroidКоришћење CPU, активност нити, tracesТражење блокирања главне нити
Android Profiler (Memory)AndroidHeap dump, праћење алокацијаТражење цурења, анализа објеката
Android Profiler (Network)AndroidСаобраћај, брзина, време захтеваОптимизација мрежних позива
LeakCanaryAndroidАутоматско откривање цурења меморијеУ свим фазама развоја
StrictModeAndroidДиск/мрежа на главној нити, цурењаDebug верзија
Traceview / SystraceAndroidПраћење метода, системски догађајиДубока анализа кашњења

Instruments (Xcode) — најмоћнији алат за iOS. Time Profiler показује које функције троше највише CPU. Allocations прати стварање и ослобађање објеката. Leaks аутоматски проналази retain cycles. Кораци профилисања: (1) покрените Instruments; (2) изаберите шаблон (Time Profiler за CPU); (3) извршите проблематични сценарио; (4) анализирајте стек позива — најшира колона је нај"топлија" функција.

Android Profiler је уграђен у Android Studio (View → Tool Windows → Profiler). CPU Profiler показује оптерећење сваке нити. Memory Profiler — heap dump и праћење алокација. Network Profiler — све HTTP захтеве са временским подацима. Energy Profiler — потрошња енергије: WakeLock, Location, Network. За детаљно праћење користи се Systrace (Android 10+) или Perfetto — системско праћење са микросекундном прецизношћу.

Покретање апликације (Cold/Warm/Hot Start)

Покретање апликације је један од кључних показатеља перформанси. Дели се на три типа: Cold Start — апликација се покреће од нуле: процес се ствара, Application.onCreate (Android) / AppDelegate.applicationDidFinishLaunching (iOS), учитавање класа, иницијализација библиотека. Warm Start — процес постоји, али је Activity/ViewController уништен (нпр. при ротацији екрана или повратку из меморије). Hot Start — Activity/ViewController је у меморији, апликација се једноставно приказује (пребацивање са друге апликације).

Cold Start је најважнија метрика. На Android-у укључује: (1) покретање Activity — учитавање XML, иницијализација View; (2) први кадар — време до првог рендеровања. Google препоручује: покретање Activity < 200 ms, први кадар < 500 ms, TTI < 5 секунди. Оптимизација Cold Start-а: смањите Application.onCreate (корутине за лењу иницијализацију), користите SplashScreen API (Android 12+), одложите иницијализацију библиотека (WorkManager, DI), уклоните непотребне ContentProviders.

На iOS-у, Cold Start укључује: учитавање Mach-O бинарног фајла, dyld (динамички линкер), иницијализација Objective-C runtime-а, application delegate, први контролер. Chrome Custom Tabs (Android) и Universal Links (iOS) — технологије за брзо отварање спољног садржаја у апликацији без потпуног Cold Start-а. Препоручује се тестирање Cold Start-а на правим уређајима средњег сегмента.

Оптимизација величине

Величина апликације — фактор перформанси за инсталацију и ажурирања. Утиче на конверзију: сваких 10 MB смањује конверзију за 1%. Google Play препоручује величину APK мању од 150 MB; App Store — мању од 200 MB (мобилне мреже — 100 MB). Главне методе оптимизације: компресија слика (WebP уместо PNG штеди 25-35%), векторизација (VectorDrawable на Android-у, SF Symbols на iOS-у), уклањање некоришћеног кода (R8/ProGuard), уклањање некоришћених ресурса (lint → unused resources).

App Bundle (Android) — формат објављивања у којем Google Play генерише оптимизовани APK за сваки уређај. App Bundle смањује величину преузимања за 20-40%. Dynamic Delivery — модули који се преузимају на захтев (on-demand feature modules). На iOS-у еквивалент су On-Demand Resources (ODR): ресурси који се преузимају након првог покретања (нивои игре, видеи).

Lazy Loading — техника у којој се модули и библиотеке не учитавају при покретању, већ се учитавају по потреби. Split APK (Android) и App Slicing (iOS) — подела апликације на архитектурне слотове: arm64-v8a, x86_64. Оптимизација величине апликације — континуиран процес: анализирајте састав APK-а (Analyze APK у Android Studio-у), уклоните дупликате икона, користите SVG уместо више густина PNG-а. У IT Sectr-у укључујемо проверу величине израде у CI/CD за сваки MR.

Често постављана питања

Шта је ANR и како га избећи?

ANR (Application Not Responding) — дијалог који се појављује на Android-у ако је главна нит блокирана више од 5 секунди. Да бисте избегли ANR, преместите све тешке операције (мрежа, база података, обрада фајлова) у позадинске нити. Аналог на iOS-у — frozen UI, када апликација престане да реагује на додире.

Шта је цурење меморије и Retain Cycle?

Цурење меморије — када објекат не може бити ослобођен јер и даље постоје референце на њега. Retain Cycle — ситуација у iOS/Objective-C-у када два објекта референцирају један другог (A → B → A) и ARC не може ослободити ниједан. Решење: weak/unowned референце и правовремено чишћење callback-ова.

Које алате користити за профилисање?

За iOS: Instruments (Time Profiler, Allocations, Leaks). За Android: Android Profiler (CPU, Memory, Network), LeakCanary (цурење меморије), StrictMode (кршење нити). Препоручује се комбиновање профилисања у фази развоја и интеграције.

По чему се Cold Start разликује од Warm Start-а и Hot Start-а?

Cold Start — апликација се покреће од нуле: процес се ствара, класе се учитавају, Application.onCreate се извршава. Warm Start — процес постоји, али се Activity/ViewController поново креира. Hot Start — Activity/ViewController је већ у меморији, само се приказује. Cold Start је најспорији (1-5 секунди) и критичан је за корисничко искуство.

Како смањити величину мобилне апликације?

Главне методе: уклоните некоришћене ресурсе и код (користите R8/ProGuard), векторизујте слике (VectorDrawable, SF Symbols), компримујте PNG/WebP (Android), користите App Bundle уместо APK, уклоните непотребне библиотеке, користите Lazy Loading за модуле. Оптимизација величине може смањити APK за 40-60%.

Резиме

  • ANR и Crash — главни проблеми стабилности; решавају се позадинским нитима и crash извештачима
  • Цурење меморије и Retain Cycle — главни узроци OOM; решавају се слабим референцама и LeakCanary-јем
  • GC (паузе Stop-the-World) vs ARC (без паузе, али retain cycles) — различити меморијски модели
  • Профилисање — обавезна фаза: Instruments (iOS), Android Profiler, LeakCanary, StrictMode
  • Cold Start — кључна метрика; оптимизација Application.onCreate и лења иницијализација
  • App Bundle и WebP/VectorDrawable — главни алати за смањење величине за 20-60%
  • Перформансе су непрекидан процес, а не једнократна активност; интегришите метрике у CI/CD

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту