Aplikacja mobilna działa wolno — jak znaleźć przyczynę i ją przyspieszyć

Wolny start, długie ładowanie, zacinanie i zawieszenia — jak zmierzyć wydajność aplikacji w Android vitals i Xcode Organizer, jakie przyczyny spotykamy najczęściej, w jakiej kolejności je naprawiać i ile to trwa.

Zespół Mobilesoft· Engineering team· 28 października 2026· 10 min czytania

„Aplikacja długo się ładuje”, „zacina się przy przewijaniu”, „po kilku minutach robi się coraz wolniejsza” — takie recenzje rzadko dotyczą jednego błędu. Wolna aplikacja to zwykle suma kilku problemów, które narastały latami: nowe funkcje, kolejne biblioteki, większe zdjęcia, więcej danych z serwera. Każda zmiana z osobna była niewielka, razem dają aplikację, z której użytkownicy korzystają niechętnie albo wcale.

Wydajność przekłada się na pieniądze bardziej, niż się wydaje. Użytkownik, który czeka kilka sekund na start aplikacji, częściej ją zamyka, rzadziej kończy zakup i chętniej wystawia niską ocenę. Na Androidzie słaba wydajność ma też bezpośredni wpływ na widoczność w Google Play.

Ten tekst opisuje, jak ustalić, co dokładnie spowalnia aplikację, jakie przyczyny spotykamy najczęściej i w jakiej kolejności je naprawiać.

Co znaczy „wolno”

Zanim zaczniesz szukać przyczyny, nazwij objaw. „Wolno” może oznaczać pięć różnych problemów, a każdy ma inne źródło:

  • Wolny start — od dotknięcia ikony do pierwszego użytecznego ekranu mija kilka sekund.
  • Długie ładowanie danych — ekran się pojawia, ale treść doczytuje się długo albo kręci się kółko.
  • Zacinanie (jank) — przewijanie listy i animacje nie są płynne, aplikacja „szarpie”.
  • Zawieszenia — aplikacja na sekundę lub dłużej przestaje reagować na dotyk. Na Androidzie po 5 sekundach system pokazuje komunikat „Aplikacja nie odpowiada” (ANR).
  • Spowalnianie w czasie — świeżo uruchomiona aplikacja działa dobrze, ale po kilkunastu minutach jest coraz wolniejsza albo się wyłącza.

Pierwsze dwa problemy użytkownicy opisują jako „długo się ładuje”, trzy kolejne jako „zacina się”. Dlatego same recenzje nie wystarczą — potrzebne są pomiary.

Krok 1: zmierz, zanim zaczniesz naprawiać

Najczęstszy błąd to optymalizowanie na wyczucie: ktoś przepisuje fragment kodu, który „na pewno jest wolny”, a aplikacja działa tak samo. Zacznij od danych, które już masz za darmo:

  • Android vitals w Google Play Console — czas uruchamiania (zimny, ciepły i gorący start), odsetek ANR, wolne i zamrożone klatki. Dane pochodzą od prawdziwych użytkowników i są pogrupowane według modelu telefonu i wersji systemu.
  • Xcode Organizer (iOS) — czas uruchamiania, zawieszenia (hangs), zużycie pamięci i baterii, również od prawdziwych użytkowników.
  • Firebase Performance Monitoring albo Sentry — czas ładowania poszczególnych ekranów i czasy odpowiedzi zapytań do API widziane z telefonu.

Orientacyjne progi, powyżej których warto działać:

Metryka Gdzie sprawdzić Kiedy to problem Zimny start Android vitals, Xcode Organizer Android uznaje za nadmierny start od 5 s; na iOS warto celować w pierwszą klatkę poniżej 0,4 s Ciepły / gorący start (Android) Android vitals od 2 s / od 1,5 s ANR (Android) Android vitals powyżej 0,47% użytkowników dziennie — próg złego zachowania Google Play Zamrożone klatki Android vitals, Sentry klatki renderowane dłużej niż 700 ms Zawieszenia (iOS) Xcode Organizer wątek główny zablokowany na 250 ms i dłużej Czas odpowiedzi API Firebase Performance, Sentry, logi backendu gdy 95. percentyl przekracza 1–2 s na kluczowych ekranach

Zwróć uwagę na rozkład według urządzeń. Aplikacja, która działa świetnie na nowym iPhonie i flagowym Samsungu, może być ledwo używalna na tańszych telefonach z Androidem, a tych w Polsce jest dużo. Jeśli zespół testuje tylko na własnych, nowych telefonach, ten problem pozostaje niewidoczny.

Dopiero gdy wiesz, który ekran i które urządzenia są problemem, sięgnij po narzędzia do szczegółowej analizy: profiler w Android Studio i Perfetto, Instruments w Xcode (Time Profiler, Allocations, Leaks), Flutter DevTools albo profiler React Native.

Najczęstsze przyczyny

Objaw Częsta przyczyna Typowa poprawka Wolny start inicjalizacja wielu SDK na starcie, synchroniczne zapytania przed pierwszym ekranem leniwa inicjalizacja, Baseline Profiles, ekran startowy bez czekania na sieć Długie ładowanie ekranu wolne API, wiele zapytań zamiast jednego, brak cache poprawka po stronie backendu, łączenie zapytań, cache i paginacja Wolne obrazki, duże zużycie danych zdjęcia w pełnej rozdzielczości, brak cache obrazów miniatury po stronie serwera lub CDN, WebP/AVIF, biblioteka cache Zacinanie przy przewijaniu ciężka praca na głównym wątku, nieefektywne listy przeniesienie pracy do tła, poprawne listy i klucze elementów Zawieszenia, ANR zapis do bazy, parsowanie JSON lub operacje na plikach na głównym wątku asynchroniczność, StrictMode do wykrywania Spowalnianie w czasie wycieki pamięci, rosnący cache w pamięci analiza pamięci, poprawa cyklu życia ekranów Wolno na starszych telefonach stary framework lub silnik, za ciężkie animacje aktualizacja frameworka, uproszczenie efektów

Poniżej szczegóły tych, które spotykamy najczęściej.

Wolny start aplikacji

Start to pierwsze wrażenie i jedyny moment, w którym użytkownik na pewno czeka. W starszych aplikacjach zwykle wygląda to tak: przy uruchomieniu inicjalizuje się kilkanaście bibliotek — analityka, reklamy, czat, zdalna konfiguracja, raporty awarii, SDK partnerów — a dopiero potem pojawia się pierwszy ekran. Do tego aplikacja czeka na odpowiedź serwera, zanim cokolwiek pokaże.

Co pomaga:

  • Inicjalizuj tylko to, co potrzebne do pierwszego ekranu. Resztę uruchamiaj później albo dopiero przy pierwszym użyciu. Czat potrzebny na jednym ekranie nie musi startować razem z aplikacją.
  • Nie czekaj na sieć przed pokazaniem ekranu. Pokaż ostatnio zapisane dane albo szkielet ekranu, a świeże dane doczytaj w tle.
  • Na Androidzie dodaj Baseline Profiles. To lista kodu, który system kompiluje z wyprzedzeniem przy instalacji. Według Google przyspiesza wykonanie kodu o około 30% już od pierwszego uruchomienia — przy niewielkim nakładzie pracy.
  • Usuń nieużywane biblioteki. W aplikacjach rozwijanych latami regularnie znajdujemy SDK, z których nikt już nie korzysta, a które wciąż spowalniają start i powiększają aplikację.

Sieć i backend: często to nie wina aplikacji

Duża część „wolnych aplikacji” to w rzeczywistości wolne API. Telefon pokazuje kółko, bo czeka na serwer — i żadna optymalizacja kodu aplikacji tego nie zmieni.

Typowe problemy po stronie komunikacji:

  • Wiele zapytań przy otwarciu jednego ekranu. Ekran zamówienia pobiera osobno zamówienie, każdy produkt, adres i status dostawy — kilkanaście zapytań, każde z opóźnieniem sieci komórkowej. Jedno zapytanie zwracające wszystko, czego ekran potrzebuje, potrafi skrócić ładowanie kilkukrotnie.
  • Brak paginacji. Lista pobiera wszystkie rekordy naraz. Przy 50 rekordach z czasów MVP to nie było problemem, przy 5000 — jest.
  • Brak cache. Aplikacja pobiera te same dane przy każdym wejściu na ekran, zamiast pokazać zapisane i odświeżyć je w tle.
  • Wolne zapytania do bazy na serwerze. Brak indeksów albo zapytania, które rosły razem z bazą. To widać w logach backendu, a nie w aplikacji.

Dlatego czasy odpowiedzi API warto mierzyć z dwóch stron: z telefonu (Firebase Performance, Sentry) i z serwera. Jeśli serwer odpowiada szybko, a telefon widzi długi czas — problem jest w sieci, rozmiarze odpowiedzi albo liczbie zapytań.

Obrazy

Zdjęcia to najczęstsza przyczyna wolnego ładowania w aplikacjach sklepowych, gastronomicznych i ogłoszeniowych. Klasyczny przypadek: serwer wysyła zdjęcie produktu w oryginalnym rozmiarze — 4000 pikseli szerokości i kilka megabajtów — a aplikacja wyświetla je jako miniaturę 200 pikseli na liście.

Poprawka zwykle nie wymaga dużych zmian w aplikacji: serwer albo CDN generuje kilka rozmiarów zdjęcia w nowoczesnym formacie (WebP lub AVIF), a aplikacja pobiera ten, który pasuje do ekranu. Do tego biblioteka z cache obrazów — Coil lub Glide na Androidzie, Kingfisher lub SDWebImage na iOS, cached_network_image we Flutterze — żeby to samo zdjęcie nie było pobierane przy każdym przewinięciu listy.

Zacinanie i zawieszenia: praca na głównym wątku

Interfejs aplikacji rysuje się na jednym, głównym wątku. Żeby animacja była płynna przy 60 klatkach na sekundę, każda klatka musi zmieścić się w około 16 ms — a na ekranach 120 Hz w połowie tego czasu. Jeśli w tym czasie aplikacja zapisuje dane do bazy, parsuje duży plik JSON albo przelicza coś skomplikowanego, klatka się spóźnia i użytkownik widzi szarpnięcie. Gdy taka operacja trwa kilka sekund, na Androidzie pojawia się ANR.

Najczęstsze źródła:

  • operacje na bazie danych i plikach wykonywane na głównym wątku,
  • parsowanie dużych odpowiedzi z serwera w wątku interfejsu,
  • listy, które przy każdym przewinięciu przeliczają albo przebudowują wszystkie elementy (w Jetpack Compose i SwiftUI — brak stabilnych kluczy i zbyt częste odświeżanie widoków),
  • ciężkie efekty graficzne: cienie, rozmycia, przezroczystości nałożone na siebie.

Na Androidzie takie problemy pomaga wyłapać StrictMode w wersjach deweloperskich — zgłasza operacje dyskowe i sieciowe na głównym wątku. Jeśli aplikacja ma dużo starego kodu widoków, warto rozważyć stopniową migrację do Jetpack Compose i SwiftUI dla najczęściej używanych ekranów.

Wycieki pamięci

Jeśli aplikacja działa dobrze po uruchomieniu, a po kilkunastu minutach zwalnia albo się wyłącza, najczęstszą przyczyną są wycieki pamięci: ekrany, które po zamknięciu nie są zwalniane, bo coś wciąż trzyma do nich odwołanie — nasłuchiwacz zdarzeń, timer, subskrypcja danych. Pamięć rośnie, system coraz częściej sprząta, aplikacja zwalnia, a w końcu system ją zamyka.

To jeden z tych problemów, których nie widać w krótkich testach. Pomagają LeakCanary na Androidzie, Instruments (Leaks, Allocations) na iOS i dane o zużyciu pamięci z Xcode Organizer. Zamknięcia aplikacji z powodu braku pamięci warto też śledzić w monitoringu awarii — często są tam widoczne jako awarie bez wyraźnej przyczyny w kodzie.

Stary framework i biblioteki

Aplikacje cross-platform sprzed kilku lat często są wolniejsze nie przez błędy w kodzie, ale przez wersję frameworka:

  • React Native — od wersji 0.70 domyślnym silnikiem JavaScript jest Hermes, a od 0.76 domyślna jest nowa architektura. Aplikacja na starym silniku i starej architekturze startuje wolniej i gorzej radzi sobie z płynnością.
  • Flutter — nowy silnik renderowania Impeller usunął typowe zacinanie przy pierwszym wyświetleniu animacji. Na iOS jest domyślny od Fluttera 3.10, na Androidzie od 3.27.

W takim przypadku sama aktualizacja frameworka potrafi dać więcej niż tygodnie ręcznej optymalizacji. Aktualizacja po latach to jednak osobny projekt i warto go zaplanować razem z testami.

W jakiej kolejności naprawiać

  1. Zmierz stan obecny — Android vitals, Xcode Organizer, czasy kluczowych ekranów. Zapisz liczby, żeby po zmianach móc je porównać.
  2. Wybierz dwa–trzy najważniejsze ekrany — start, lista produktów, koszyk, płatność. Poprawa ekranu, który widzi 90% użytkowników, jest warta więcej niż dopracowanie ekranu ustawień.
  3. Zacznij od szybkich poprawek — rozmiary zdjęć, cache, leniwa inicjalizacja SDK, łączenie zapytań, Baseline Profiles. Często dają większość efektu.
  4. Potem praca na głównym wątku i wycieki pamięci — wymagają profilowania, ale usuwają zacinanie i ANR.
  5. Aktualizacja frameworka lub przebudowa ekranów — jeśli pomiary pokazują, że ograniczeniem jest technologia, a nie konkretne fragmenty kodu.
  6. Pilnuj, żeby nie wróciło. Dodaj pomiar czasu startu i kluczowych ekranów do procesu wydań — na Androidzie służy do tego biblioteka Macrobenchmark, którą można uruchamiać w CI/CD.

Poprawki wydawaj stopniowo i porównuj metryki nowej wersji z poprzednią. Lepsze wyniki powinny być widoczne w Android vitals i Xcode Organizer po kilku dniach od wydania.

Ile to trwa

Etap Orientacyjny czas Pomiary i diagnoza (audyt wydajności) 3–5 dni Szybkie poprawki: obrazy, cache, start, łączenie zapytań 1–2 tygodnie Główny wątek, listy, wycieki pamięci 1–3 tygodnie, zależnie od liczby ekranów Aktualizacja frameworka lub przebudowa kluczowych ekranów od kilku tygodni, osobny projekt

Jeśli wydajność jest tylko jednym z wielu problemów — aplikacja jest też niestabilna, trudna w rozwoju i oparta na przestarzałych bibliotekach — warto spojrzeć szerzej i ocenić, czy lepszy będzie refactor, czy przepisanie aplikacji.

Podsumowanie

Wolna aplikacja rzadko ma jedną przyczynę. Najczęściej to połączenie kilku: zbyt wielu bibliotek startujących razem z aplikacją, wolnego lub źle używanego API, zdjęć w pełnej rozdzielczości, pracy na głównym wątku i starego frameworka. Zacznij od pomiarów, które Google i Apple udostępniają za darmo, wybierz najważniejsze ekrany i naprawiaj od zmian, które dają najwięcej przy najmniejszym nakładzie. Lepsza wydajność to zwykle także lepsza ocena w sklepie.

Jeśli Twoja aplikacja działa wolno i nie wiesz, od czego zacząć, zamów bezpłatną analizę aplikacji — sprawdzimy dane z Android vitals i Xcode Organizer i wskażemy, co spowalnia ją najbardziej. Szczegółowy przegląd kodu i wydajności prowadzimy w ramach audytów i konsultacji, a stałą opiekę — w ramach utrzymania aplikacji mobilnych.