Оптимізація мережевих запитів: як скоротити час завантаження екрана?
Оптимізація мережевих запитів мобільного застосунку включає кешування запитів, HTTP/2 мультиплексування, батчинг запитів та дедуплікацію. Головний екран застосунку робить 14 паралельних запитів при відкритті. Здавалося б — паралельно, значить швидко. Але HTTP/1.1 обмежений 6 з'єднаннями до одного хоста, і 8 запитів стоять у черзі. На слабкому LTE з RTT 180 мс — сумарне очікування до готовності екрана перевищує 2 секунди. Кожна секунда затримки знижує конверсію на 7%, а утримання — на 20%. Втрати виручки від повільних екранів можуть становити мільйони гривень щомісяця.
Перехід на HTTP/2 з мультиплексуванням або агрегація запитів на BFF-шарі (Backend for Frontend) вирішує це без змін у клієнтському коді. HTTP/2 — протокол, який мультиплексує запити в одному з'єднанні, усуваючи блокування черги (head-of-line blocking). Наприклад, HTTP/2 з мультиплексуванням працює в 3-4 рази швидше за HTTP/1.1. Наші інженери застосовують обидва підходи залежно від архітектури.
Де губиться час?
Зайві запити. Найчастіше — відсутність кешування на клієнті. URLSession на iOS за замовчуванням поважає Cache-Control заголовки, але лише якщо сервер їх виставляє. Якщо API повертає Cache-Control: no-store «для надійності» — кожне звернення до довідкових даних (категорії, налаштування, конфігурація) йде по мережі. URLCache з лімітом 50 MB і ручне встановлення URLRequest.cachePolicy = .returnCacheDataElseLoad для read-only ендпоінтів працює як швидкий патч.
Надмірні payload. REST-ендпоінт для списку користувачів повертає 40 полів, з яких UI використовує 4. На списку зі 100 елементів це зайві 60–80 KB JSON на кожен запит. GraphQL вирішує це на рівні протоколу, але якщо GraphQL немає — ?fields=id,name,avatar_url як query-параметр фільтрації хоча б частково рятує ситуацію.
Повторні запити при обертанні екрана. На Android ViewModel + LiveData/StateFlow тримають результат запиту і не перезапускають його при перестворенні Activity. Але якщо запит живе у Fragment.onViewCreated без перевірки — при кожному обертанні йде новий мережевий виклик. Діагностується через Charles Proxy або OkHttp EventListener з логуванням.
Інструменти та рішення: який підхід обрати?
iOS (URLSession / Alamofire / Moya)
Alamofire RequestInterceptor — зручне місце для retry-логіки з exponential backoff:
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { let delay = min(pow(2.0, Double(request.retryCount)), 30.0) completion(.retryWithDelay(delay)) } URLSession з waitsForConnectivity = true — запит автоматично чекає відновлення мережі замість негайної помилки. Критично для offline-first застосунків.
Android (OkHttp / Retrofit)
OkHttp CacheInterceptor вже вбудований, достатньо передати Cache при створенні клієнта:
val cache = Cache(context.cacheDir, 50L * 1024 * 1024) val client = OkHttpClient.Builder().cache(cache).build() Retrofit + suspend fun — автоматичне скасування запиту при смерті coroutine scope. Головне — прив'язувати scope до viewModelScope, а не до GlobalScope.
Дедуплікація запитів
Якщо кілька компонентів одночасно запитують один ресурс — виконувати запит один раз. На iOS — Combine з share() оператором на Publisher. На Android — StateFlow в Repository: перший підписник запускає запит, інші отримують результат з того ж flow. Це відповідає рекомендаціям Apple Human Interface Guidelines щодо чуйності.
Request prioritization
На iOS URLSession підтримує URLRequest.networkServiceType: .responsiveData для дій користувача, .background для аналітики та prefetch. Система пріоритезує трафік відповідно — аналітика не конкурує за bandwidth з користувацьким запитом.
На Android WorkManager з NetworkType.CONNECTED і пріоритетом EXPEDITED vs стандартним — для фонової синхронізації даних без блокування основного потоку запитів.
Налаштування кешування: покрокова інструкція
- Визначте read-only ендпоінти (довідники, категорії, конфігурація).
- Перевірте, чи виставляє сервер Cache-Control. Якщо ні — налаштуйте клієнтське кешування примусово.
- На iOS встановіть URLCache з лімітом 50 MB і вкажіть cachePolicy = .returnCacheDataElseLoad.
- На Android створіть OkHttp Cache розміром 50 MB і передайте в OkHttpClient.
- Для динамічних даних використовуйте ETag або Last-Modified: клієнт відправляє If-None-Match, сервер повертає 304 Not Modified, економлячи трафік.
Порівняння HTTP/1.1 та HTTP/2
| Характеристика | HTTP/1.1 | HTTP/2 |
|---|---|---|
| Кількість з'єднань на хост | 6 (зазвичай) | 1 (мультиплексування) |
| Head-of-line blocking | Так (черга запитів) | Ні (потоки всередині з'єднання) |
| Сервер push | Ні | Так (server push) |
| Стиснення заголовків | Ні (HPACK) | Ні (HPACK) |
| Час завантаження на слабкому LTE (14 запитів) | >2 с | ~600 мс |
HTTP/2 дає виграш у 3-4 рази на слабких мережах за рахунок усунення черг.
Приклад: GraphQL N+1 на мобілі
З нашої практики: один із клієнтів використовував GraphQL, але запити будувались «як зручно» — окремий query на кожну картку в списку при детальному перегляді. 20 карток = 20 запитів. Впровадження DataLoader-патерну на клієнті через @defer directive (Apollo iOS / Apollo Android підтримують) дозволило батчити запити. Час завантаження детального екрана — з 2.8 с до 0.6 с. Досвід нашої команди показує, що такий підхід застосовний і для REST через BFF.
Порівняння стратегій кешування
| Підхід | iOS | Android | Зниження трафіку | Складність |
|---|---|---|---|---|
| URLCache / OkHttp Cache | URLCache + cachePolicy | Cache в OkHttp | до 70% | Низька |
| Disk-based + memory | + | + | до 90% | Середня |
| ETag / Last-Modified | URLSession за замовчуванням | OkHttp за замовчуванням | до 50% при 304 | Низька |
| Pragma / Cache-Control | Налаштовується | Cache-Control | до 80% | Середня |
Типові помилки при оптимізації мережевих запитів
- Забувають налаштувати Cache-Control на сервері, через що кешування не працює. - Використовують один і той самий HTTP-клієнт для всіх запитів без урахування пріоритетів. - Не перевіряють поведінку на слабких мережах — емулятор LTE обов'язковий. - Роблять запити при кожному малюванні екрана, а не при створенні ViewModel.Обсяг робіт
- Аудит поточного мережевого шару з вимірюванням часу та аналізом трафіку.
- Впровадження HTTP/2 або агрегації на BFF.
- Налаштування кешування (URLCache, OkHttp Cache, ETag).
- Дедуплікація та батчинг запитів.
- Оптимізація payload (GraphQL або field-фільтрація).
- Retry-логіка та prioritization.
- Документація та рекомендації щодо подальшої підтримки.
Вартість аудиту — від 50 000 грн. Економія на трафіку після впровадження кешування може сягати $500 на місяць. Зв'яжіться з нами, щоб замовити аудит мережевого шару — оцінимо ваш проект за 1 день.
Наш досвід та переваги
У нас 5+ років досвіду в оптимізації мобільних застосунків для iOS та Android. Ми виконали понад 50 проектів із прискорення завантаження, кешування та зниження трафіку. Наприклад, один із клієнтів після нашого впровадження кешування та HTTP/2 зекономив $400 на місяць на трафіку. Гарантуємо, що після аудиту ви отримаєте конкретні рекомендації з вимірними метриками. Вартість аудиту — від 50 000 грн. Залиште заявку на аудит і отримайте детальний звіт із метриками.
Терміни
Аудит мережевого шару та точкові оптимізації — 3–5 днів. Впровадження кешування, retry-логіки та дедуплікації по всьому застосунку — 1–2 тижні. Отримайте консультацію — ми розрахуємо точні терміни під ваш проект.







