Paginacja w rozwoju mobilnym — co to jest, rodzaje i zasada działania

Autor: IT Sectr Opublikowano: 2026-03-11 Czas czytania: 9 min

Paginacja — technika ładowania danych strona po stronie, stosowana w aplikacjach mobilnych i serwisach internetowych do pracy z dużymi zbiorami rekordów. Według Android Developers Documentation (2025), prawidłowa implementacja paginacji zmniejsza obciążenie API, oszczędza transfer i poprawia doświadczenie użytkownika. Ładowanie stronicowe pozwala aplikacji wyświetlać treści stopniowo, bez oczekiwania na pełne załadowanie wszystkich danych.

Najważniejsze

  • Paginacja — sposób ładowania dużych zbiorów danych porcjami w celu optymalizacji wydajności i transferu.
  • Paginacja Offset używa przesunięcia (page/offset) do nawigacji — prosta, ale niestabilna metoda przy częstych wstawieniach.
  • Paginacja Cursor używa unikalnego kursora z ostatniego rekordu — stabilna przy zmianach danych między zapytaniami.
  • Paginacja Keyset filtruje po kolumnie z unikalnym indeksem — wydajna dla dużych tabel bez duplikatów.
  • Paginacja Time-based grupuje rekordy według znaczników czasu — wygodna dla kanałów wiadomości i mediów społecznościowych.

Co to jest paginacja?

Paginacja (od ang. pagination — podział na strony) — to technika dzielenia dużego zbioru danych na kolejne porcje (strony). W aplikacjach mobilnych paginacja jest używana przy ładowaniu list wiadomości, kanałów informacyjnych, katalogów produktów, historii zamówień i wszelkich innych kolekcji z potencjalnie nieograniczoną liczbą rekordów.

Bez paginacji aplikacja musi ładować wszystkie dane naraz, co prowadzi do długiego oczekiwania, dużego zużycia transferu i niestabilnej pracy na słabych urządzeniach. Zapytanie API z paginacją zwraca tylko jedną porcję danych i metainformacje do załadowania następnej — w ten sposób aplikacja kontroluje ilość otrzymywanych informacji.

Główne metryki paginacji: rozmiar strony (page size) — liczba rekordów na jednej stronie (zwykle 10-50), oraz numer strony lub kursor — wskaźnik aktualnej pozycji w zbiorze. Wybór rozmiaru strony zależy od typu danych: dla kompaktowych elementów (nazwy) wystarczy 20-30, dla kart z obrazkami — 10-15.

Po co potrzebna jest paginacja w aplikacjach mobilnych

Urządzenia mobilne mają ograniczone zasoby: ilość pamięci RAM, szybkość procesora i limity transferu. Paginacja rozwiązuje trzy kluczowe zadania: zmniejszenie zużycia pamięci (w pamięci przechowywane są tylko widoczne elementy), przyspieszenie pierwszego wyświetlenia (pierwsza porcja ładuje się szybciej niż cały zbiór) i oszczędność transferu (dane są ładowane tylko gdy użytkownik przewija listę).

Główne rodzaje paginacji

Istnieją cztery główne rodzaje paginacji, z których każdy rozwiązuje specyficzne zadania. Wybór metody zależy od wymagań dotyczących spójności danych, architektury API, typu przechowywania i dopuszczalnej złożoności implementacji po stronie klienta i serwera.

TypZasada działaniaStabilnośćSzybkość przy dużych wolumenach
OffsetLIMIT + OFFSET w SQLniskaspada ze wzrostem OFFSET
CursorWHERE id > last_idwysokastabilna (O(log n))
KeysetWHERE key > last_keywysokastabilna (O(log n))
Time-basedWHERE created_at < last_timeśredniastabilna z indeksem

Kiedy używać którego typu

Paginacja Offset nadaje się do statycznych lub rzadko aktualizowanych zbiorów danych, gdy ważna jest prosta implementacja. Cursor i Keyset — dla dynamicznych danych z częstymi wstawieniami. Time-based — dla chronologicznych kanałów, gdzie rekordy są uporządkowane według czasu utworzenia. Standard GraphQL Relay używa paginacji cursorowej jako jedynej zalecanej metody.

Paginacja Offset: zalety i wady

Paginacja Offset — najprostszy rodzaj ładowania stronicowego. Klient przekazuje parametry page i limit (lub offset i limit), serwer stosuje SQL OFFSET i LIMIT. Na przykład page=2, limit=20 zwróci rekordy od 21 do 40. Ta metoda jest intuicyjnie zrozumiała i łatwa do zaimplementowania na dowolnym stosie technologicznym.

python
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items")
async def get_items(
    page: int = Query(default=1, ge=1),
    limit: int = Query(default=20, le=100)
):
    offset = (page - 1) * limit
    items = await fetch_items(offset, limit)
    total = await count_items()
    return {
        "items": items,
        "total": total,
        "page": page,
        "pages": (total + limit - 1) // limit
    }

Problem niespójności danych

Główna wada paginacji Offset — problem pominiętych i duplikujących się rekordów. Jeśli między dwoma zapytaniami do tabeli zostaną dodane nowe rekordy, OFFSET się przesuwa: użytkownik może zobaczyć ten sam rekord dwa razy lub pominąć nowy. Jest to krytyczne dla kanałów informacyjnych i czatów, gdzie ważna jest spójność.

Inny problem — spadek wydajności przy dużych OFFSET. Baza danych musi skanować i pomijać pierwsze offset rekordów przed zwróceniem wyniku. Przy offset=100000 nawet z LIMIT 20 serwer spędzi zauważalny czas na skanowaniu. PostgreSQL i MySQL wykazują liniowy spadek prędkości ze wzrostem OFFSET.

Kiedy Offset wciąż jest dobry

Paginacja Offset pozostaje najlepszym wyborem dla: panelów administracyjnych (dane zmieniają się rzadko, potrzebna nawigacja po stronach), raportów i logów historycznych (stały wycinek danych), katalogów z filtrowaniem (można przejść do dowolnej strony). Offset jest też najłatwiejszy do zaimplementowania po stronie klienta — RecyclerView z Paging 3 obsługuje go od razu.

Paginacja Keyset i Time-based

Paginacja Keyset używa unikalnego klucza (zwykle klucza głównego) do filtrowania rekordów. Zamiast OFFSET zapytanie używa WHERE id > last_seen_id. Zapewnia to stabilną wydajność niezależnie od liczby rekordów i brak duplikatów przy wstawieniach, ponieważ nowe rekordy zawsze mają większe id.

sql
-- Paginacja Offset (problematyczna)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;

-- Paginacja Keyset (stabilna)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;

Paginacja Time-based

Paginacja Time-based (lub kursor czasu) używa znacznika czasu created_at do nawigacji. Klient przekazuje timestamp ostatnio załadowanego rekordu, serwer zwraca rekordy utworzone przed lub po tym znaczniku. Metoda jest popularna w mediach społecznościowych i kanałach informacyjnych, gdzie kolejność rekordów określa czas publikacji.

Cechą paginacji Time-based są możliwe duplikaty, jeśli dwa rekordy zostały utworzone w tej samej milisekundzie. Aby wyeliminować ten problem, łączy się klucz time-based z unikalnym id: WHERE (created_at, id) < (last_time, last_id). Taki złożony kursor gwarantuje unikalność każdego rekordu i dokładną kolejność.

Porównanie Keyset i Time-based

Paginacja Keyset wymaga kolumny z unikalną i monotonicznie rosnącą wartością (autoinkrementowane id, UUID v7). Time-based nadaje się do dowolnych tabel z created_at, ale wymaga dodatkowego przetwarzania duplikatów. Główna różnica: Keyset działa stabilnie przy dowolnych operacjach wstawiania, a Time-based jest wrażliwy na identyczne znaczniki czasu.

Jak wybrać typ paginacji dla projektu

Wybór typu paginacji zależy od charakteru danych i wymagań dotyczących doświadczenia użytkownika. Poniżej znajdują się zalecenia dla typowych scenariuszy w rozwoju mobilnym. Uniwersalnego rozwiązania nie ma — każda metoda ma obszar zastosowania, w którym jest optymalna.

  • Czat / komunikator — Paginacja Cursor (po id wiadomości). Nowe wiadomości dodawane są od góry, kursor nie zbacza.
  • Kanał informacyjny — Paginacja Time-based (po created_at). Rekordy są uporządkowane według czasu, ważna jest chronologia.
  • Katalog produktów — Paginacja Offset. Użytkownik może przejść do konkretnej strony, dane zmieniają się rzadko.
  • Historia zamówień — Paginacja Cursor. Stabilność jest ważna, ponieważ nowe zamówienia są dodawane między ładowaniami.
  • Komentarze — Paginacja Keyset. Każdy komentarz ma unikalne id, duże wolumeny bez duplikatów.

Implementacja na Androidzie z Paging 3

Biblioteka Android Paging 3 obsługuje wszystkie typy paginacji przez PagingSource. Dla Offset — PagingSource z kluczem Int (page), dla Cursor — z kluczem String lub Long (cursor). PagingSource automatycznie zarządza ładowaniem, buforowaniem i ponownymi próbami przy błędach.

kotlin
class PostPagingSource(
    private val api: PostApi
) : PagingSource<Long, Post>() {

    override suspend fun load(
        params: LoadParams<Long>
    ): LoadResult<Long, Post> {
        val cursor = params.key ?: Long.MAX_VALUE
        return try {
            val response = api.getPosts(cursor, params.loadSize)
            LoadResult.Page(
                data = response.items,
                prevKey = null,
                nextKey = response.items.lastOrNull()?.id
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Long, Post>): Long? {
        return state.anchorPosition?.let {
            state.closestItemToPosition(it)?.id
        }
    }
}

Zalecenia dotyczące rozmiaru strony

Rozmiar strony wpływa na szybkość ładowania i postrzeganą wydajność. Dla aplikacji mobilnych optymalny zakres to 10-25 elementów na stronę. Mniej niż 10 — zbyt częste zapytania do API i szarpany skrol. Więcej niż 25 — długie ładowanie pierwszej porcji na wolnych sieciach.

Dla obrazków i wideo rozmiar strony zmniejsza się do 5-10, ponieważ każdy element wymaga dodatkowego czasu na ładowanie mediów. Dla list z elementami tekstowymi (komentarze, logi) rozmiar można zwiększyć do 30-50 rekordów. Zaleca się, aby rozmiar strony był konfigurowalny przez API, aby klient mógł dostosować się do różnych warunków sieciowych.

Często zadawane pytania

Co to jest paginacja prostymi słowami?

Paginacja — to ładowanie danych częściami, a nie wszystkich naraz. Jak w książce: czytasz jedną stronę, potem przewracasz na następną. W aplikacji oznacza to, że podczas przewijania listy ładuje się następna porcja danych, a nie cała lista naraz, co oszczędza transfer i pamięć.

Czym Offset różni się od Cursor paginacji?

Offset liczy rekordy: „pomiń 20, zwróć następne 10„. Jeśli między ładowaniami doda się nowy rekord — numeracja się rozjeżdża. Cursor używa unikalnego identyfikatora ostatniego rekordu: „zwróć 10 rekordów po ID = 100„. Nowe rekordy nie wpływają na pozycję.

Jaki rozmiar strony paginacji jest optymalny?

Dla aplikacji mobilnych optymalnie 10-25 elementów. Dla list z obrazkami — 5-10, dla kanałów tekstowych — 20-30. Rozmiar zależy od średniego rozmiaru jednego elementu: im cięższy element, tym mniejsza powinna być strona dla szybkiego wyświetlenia.

Jak zaimplementować paginację w RecyclerView?

Użyj biblioteki Paging 3 z Android Jetpack. Zapewnia ona PagingSource do ładowania, PagingData dla strumienia reaktywnego i PagingDataAdapter do automatycznego doładowywania podczas przewijania. Biblioteka obsługuje paginację Offset, Cursor i Keyset przez niestandardowy PagingSource.

Co to jest nieskończony skrol i czym różni się od paginacji?

Nieskończony skrol — to wzorzec UI, w którym nowa porcja danych ładuje się automatycznie przy zbliżaniu się do końca listy. Paginacja — to mechanizm ładowania danych porcjami, a nieskończony skrol — to jeden ze sposobów jego wyświetlania. Alternatywą jest przycisk „Załaduj więcej„.

Podsumowanie

  • Paginacja — technika ładowania danych porcjami, niezbędna w aplikacjach mobilnych z dowolnymi listami.
  • Paginacja Offset jest prosta w implementacji, ale cierpi na niespójność przy wstawieniach i spadek wydajności przy dużych OFFSET.
  • Paginacja Cursor używa unikalnego identyfikatora do nawigacji — stabilna i wydajna na dowolnych wolumenach.
  • Paginacja Keyset filtruje po kluczu głównym, zapewniając maksymalną wydajność dzięki użyciu indeksów.
  • Paginacja Time-based grupuje rekordy według znaczników czasu — idealna dla chronologicznych kanałów i mediów społecznościowych.
  • Wybór metody zależy od charakteru danych: dla dynamicznych — Cursor/Keyset, dla statycznych — Offset, dla kanałów — Time-based.
  • Paging 3 na Androidzie i standardowe rozwiązania cursorowe na iOS/web zapewniają gotową infrastrukturę dla dowolnego typu paginacji.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również