Оптимізація продуктивності мобільних застосунків

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 30 з 35Усі 1735 послуг
Реалізація Object Pooling для мобільної гри
Середній
від 1 дня до 3 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Оптимізація мобільних додатків: холодний старт, память, батарея, FPS, профілювання

Додаток із часом холодного старту 4+ секунди втрачає користувачів ще до першого екрану. Android Vitals в Google Play Console прямо впливає на ранжування в пошуку: додатки з поганими метриками отримують менший organic reach. Apple аналогічно моніторить crash rate та час запуску через MetricKit. Оптимізація—це не «зробити швидше», а зрозуміти де саме теряється час та що з цим робити.

Холодний старт: де теряється час до першого кадру

Холодний старт—запуск додатку, коли процес не існує в памяті. На Android це час від натиску на іконку до Activity.onWindowFocusChanged(hasFocus = true). На iOS—від tap до viewDidAppear першого екрану.

Android: головний потік перевантажений при ініціалізації

Application.onCreate()—головний враг швидкого старту на Android. Розробники ініціалізують тут все підряд: Firebase, Analytics, базу даних, HTTP-клієнт, DI-контейнер. Кожен SDK додає 20–200 мс на main thread.

Інструмент для діагностики: Android Studio Profiler → App Startup. Показує граф ініціалізації з часом кожного компонента. Альтернатива—Tracing.beginSection("MyInitTag") в коді + systrace.

Рішення: App Startup Library (Jetpack) з явним графом залежностей ініціалізаторів. Компоненти, потрібні тільки в конкретних сценаріях, ініціалізуються лінивою—by lazy {} або initializer з флагом lazyInit. Firebase Analytics, наприклад, не потрібний до першої дії користувача—його ініціалізацію можна відкласти.

ContentProviderи, автоматично додавані SDK через AndroidManifest merge, також запускаються при старті. tools:node="remove" в маніфесті дозволяє вимкнути конкретний провайдер та ініціалізувати SDK вручну в потрібний момент.

Ще одна грабелька: Room.databaseBuilder().build() на main thread. Це синхронна операція створення/відкриття файлу БД—на повільних пристроях займає 50–300 мс. Переносимо в coroutine з Dispatchers.IO, в ViewModel через viewModelScope.launch.

iOS: Dyld linking та +load

На iOS холодний старт ділиться на pre-main (до виклику main()) та post-main. Pre-main—час завантаження dylib, rebase/binding, ініціалізація Objective-C runtime та виконання методів +load.

Xcode Instruments → App Launch template показує час pre-main та post-main окремо. DYLD_PRINT_STATISTICS=1 в схемі запуску виводить детальний час в консоль.

Що вбиває pre-main:

  • Багато динамічних бібліотек (кожна dylib—накладні витрати на linkovку). CocoaPods додає окрему dylib на кожен pod. Рішення: Swift Package Manager зі статичною linkovкою (type: .static) або use_frameworks! :linkage => :static в CocoaPods.
  • Методи +load в Objective-C—виконуються синхронно при завантаженні класу, до main(). Сторонні SDK можуть зловживати цим. +initialize—ленивий аналог, викликається при першому зверненні до класу.

Post-main—application(_:didFinishLaunchingWithOptions:). Та ж історія що на Android: синхронна ініціалізація всього. lazy var для сервісів, які не потрібні негайно. SwiftUI @StateObject ініціалізує об'єкт тільки коли View з'являється—це вже вбудована ліниість.

Цільові метрики (рекомендації App Store): холодний старт < 400 мс для простих додатків, < 2 секунди для складних. Теплий старт (процес в памяті, але Activity/Scene пересоздається) < 1 секунда.

Память: витоки, OOM, excessive pressure

Витік памяти на iOS—retention cycle: об'єкт A утримує посилання на B, B утримує на A, жодне не звільняється. Класика: Timer з self в замиканні без [weak self]. Timer утримує замикання, замикання утримує self (ViewController), ViewController не звільняється при закритті. Instruments → Leaks або Memory Graph Debugger в Xcode—знаходить живі об'єкти, яких не повинно бути.

На Android garbage collector керує памяттю, але витоки все рівно трапляються. Activity або Fragment, утримувані через статичне посилання, singleton, або Handler/Runnable після onDestroy—класика. LeakCanary—обов'язковий інструмент у debug-збірці. Додається однією залежністю debugImplementation "com.squareup.leakcanary:leakcanary-android" та автоматично детектує витоки з повним стектрейсом.

OutOfMemoryError найчастіше трапляється через завантаження зображень. Bitmap в памяті займає ширина × висота × 4 байти. Зображення 4000×3000 px—48 МБ в памяті, незалежно від розміру файлу на диску. Glide / Coil правильно обробляють це: завантажують з downsampling під розмір View, кешують в LRU-кеш. Завантажувати в ImageView без Glide/Coil через BitmapFactory.decodeFile—шлях до OOM на пристроях з 2 ГБ RAM.

На Flutter Dart VM має власний GC, але нативні ресурси (зображення, текстури) не керуються Dart GC. Image.network кешує зображення в памяті без автоматичного звільнення при виході з дерева віджетів—при довгих списках з картинками використовуємо cached_network_image з правильними memCacheWidth/memCacheHeight.

FPS та UI Performance

60 FPS—16.67 мс на кадр. 120 FPS (ProMotion)—8.33 мс. Все що займає більше на main thread—джанк.

Типові причини просадок FPS:

На iOS: синхронна декодування зображень в cellForRowAt. Коли ячейка таблиці з'являється, UIImage(contentsOfFile:) декодує JPEG/PNG на main thread—видно як заторможений скролл при довгих списках. Рішення: UIImage.preparingForDisplay() (iOS 15+) або ImageIO з kCGImageSourceCreateThumbnailWithTransform в background queue, результат через DispatchQueue.main.async.

На Android: RecyclerView.Adapter.onBindViewHolder з синхронними операціями. Бази даних, файлова система, синхронні сітьові запити на main thread—StrictMode.ThreadPolicy з detectAll().penaltyLog() у debug-збірці покаже все порушення.

На Flutter: build() метод викликається часто, він повинен бути дешевим. setState() на верхньому віджеті пересобирає все дерево. const конструктори, RepaintBoundary, розбиття на дрібні віджети з локальним стейтом—основні інструменти. Flutter DevTools → Performance показує janky frames (червоні) з причинами.

Профілювання Compose: Recomposition Highlighter та трасування через Trace.beginSection в @Composable. remember для дорогих обчислень, derivedStateOf для computed values, LazyColumn замість Column + forEach для довгих списків.

Батарея: Wake locks, WorkManager, сітьові запити

Додаток в топі по розходу батареї—користувач бачить це в налаштуваннях та видаляє. Android Battery Historian (з ADB bug report) показує детальний timeline: wake locks, wakeups, network activity, sensor usage.

Основні споживачі енергії:

  • Постійний GPS (розбираємо в maps-geo)
  • Polling мережі кожні N секунд замість push
  • Утримання wake lock довше необхідного
  • Excessive AlarmManager wakeups

WorkManager з Constraints—правильний способ планувати фонові завдання: setRequiredNetworkType, setRequiresBatteryNotLow, setRequiresCharging. ОС батчирує завдання та виконує у зручний час.

На iOS BGTaskScheduler з BGProcessingTaskRequest (для важких завдань при зарядці) та BGAppRefreshTaskRequest (для легких оновлень)—система вирішує коли виконувати, розробник тільки реєструє та реалізує логіку.

Батчинг сітьових запитів: замість 10 окремих запитів протягом хвилини—один батч запит. Менше radio-активностей (LTE radio споживає багато при ініціалізації з'єднання), менше wakeups.

Інструменти профілювання

Платформа Інструмент Що показує
iOS Xcode Instruments (Time Profiler) CPU, call stack, гарячі методи
iOS Allocations Живі об'єкти, піки памяти
iOS Leaks Retention cycles
iOS MetricKit Виробничі метрики (crash rate, hang rate, launch time)
Android Android Profiler CPU, Memory, Network, Energy
Android Systrace / Perfetto System-level трейси
Android LeakCanary Витоки памяти
Android Battery Historian Енергоспоживання
Flutter Flutter DevTools Recomposition, frame rendering, memory
Flutter Dart Observatory Dart VM profiling

MetricKit на iOS—особливо цінна: реальні дані з пристроїв користувачів, а не симулятора. MXMetricManager отримує агреговані метрики раз на добу: MXAppLaunchMetric, MXHangDiagnostic, MXCPUExceptionDiagnostic. Діагностики по hang та CPU-exceptions містять стектрейс з реального пристрою—золото для діагностики production-проблем.

Процес оптимізації

Починаємо з вимірювання, не з припущень. Інструменти вище дають цифри: конкретний час холодного старту, конкретний обсяг памяти, конкретні кадри з просадкою. Потім—пріоритизація за impact: що більше за все впливає на користувацький досвід саме в цьому додатку.

Аудит продуктивності існуючого додатку: 3–5 робочих днів. Реалізація оптимізацій—від тижня до двох місяців залежно від запущеності проблем та архітектури коду.