Профілювання мережі мобільної програми
Програма працює нормально в офісному Wi-Fi, але користувачі скаржаться на повільне завантаження. Причина виявляється лише при профілюванні в реальних умовах: 47 синхронних запитів при відкритті стрічки, з яких 12 — дублюючі запити на ті самі дані, 8 — запити, які можна було б не робити взагалі (дані є в локальному кеші). Це видно лише в мережевому профайлері.
Інструменти мережевого профілювання
Charles Proxy / Proxyman
Обидва перехоплюють весь HTTP/HTTPS трафік пристрою. Charles Proxy — кросс-платформенний стандарт; Proxyman — нативний инструмент для macOS з кращим UX для розробників iOS. Налаштування: встановити кореневий сертифікат на пристрій, встановити проксі в налаштуваннях Wi-Fi.
На що дивитися в Charles:
- Дублюючи запити — той же URL кілька разів за короткий період
- Розмір відповідей — ендпоінти, що повертають явно зайві дані (10 KB там, де потрібно 500 байт)
- Час відповіді — повільні серверні відповіді vs затримка на клієнті
- Помилки та повторні спроби — скільки запитів завершується помилкою та як обробляється retry
Throttling у Charles (Proxy → Throttle Settings) — симуляція 3G, Edge, повільного Wi-Fi. Обов'язковий крок: перевірити програму на 400 Kbps перед релізом. Поведінка при поганому з'єднанні часто не тестується і містить серйозні баги.
Android Network Profiler
Вбудований в Android Studio. Показує запити хронологічно, тіло запиту/відповіді, час DNS resolution, SSL handshake, очікування, завантаження. Особливо корисен Connection View — видно, скільки паралельних з'єднань відкрито та чи є черга очікування.
Для OkHttp додайте EventListener для точних метрик:
val client = OkHttpClient.Builder()
.eventListener(object : EventListener() {
override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) {
Log.d("NET", "connectStart: ${call.request().url}")
}
override fun responseBodyEnd(call: Call, byteCount: Long) {
Log.d("NET", "responseBodyEnd: $byteCount bytes")
}
})
.build()
Xcode Network Instruments + URLSessionTaskMetrics
URLSessionTaskMetrics — вбудований механізм iOS для збору метрик кожного запиту:
func urlSession(_ session: URLSession, task: URLSessionTask,
didFinishCollecting metrics: URLSessionTaskMetrics) {
for transaction in metrics.transactionMetrics {
print("DNS: \(transaction.domainLookupEndDate! - transaction.domainLookupStartDate!)")
print("TLS: \(transaction.secureConnectionEndDate! - transaction.secureConnectionStartDate!)")
print("TTFB: \(transaction.responseStartDate! - transaction.requestStartDate!)")
}
}
Це дає розбивку: DNS lookup, TCP connect, TLS handshake, TTFB (Time to First Byte), час передачі. Якщо TLS handshake займає 300 мс на кожному запиті — або відсутні HTTP persistent connections, або Certificate Pinning неправильно налаштований без переповторення сесії.
Що аналізуємо та виправляємо
HTTP/2 multiplexing. Перевіряємо: використовується ли HTTP/2 або програма працює на HTTP/1.1 з 6 паралельними з'єднаннями? URLSession та OkHttp підтримують HTTP/2 автоматично, якщо сервер це підтримує. Видно в Charles: Protocol: h2 vs http/1.1.
Компресія. Сервер повинен повертати Content-Encoding: gzip або br (Brotli) для JSON. Якщо ні — JSON-відповіді йдуть без стиснення. Різниця для типових API-відповідей: 3–5x по розміру.
Переповторення з'єднання. TLS handshake — дорога операція (50–200 мс). Persistent connections переповторюють встановлене з'єднання. Якщо кожен запит починається з нового handshake — проблема в конфігурації URLSession (кілька екземплярів замість shared) або в серверному keepalive timeout.
Випадок: 800 мс на запити до API
Профілювання через Charles показало: кожен запит до api.example.com мав DNS lookup 120–180 мс. Причина — DNS не кешувався через короткий TTL (60 секунд) та URLSession не переповторював DNS resolution між сесіями. Рішення: URLSessionConfiguration.urlCache з користувальницьким DNS prefetch + перехід на єдиний URLSession.shared замість створення нового екземпляра в кожному сервісному класі.
Часові рамки
Мережеве профілювання та підготовка звіту — 1–2 дні. Виправлення виявлених проблем — 2–5 днів.







