Stránkování — technika postupného načítání dat po stránkách, používaná v mobilních aplikacích a webových službách pro práci s velkými sadami záznamů. Podle Android Developers Documentation (2025) správná implementace stránkování snižuje zatížení API, šetří přenos dat a zlepšuje uživatelský zážitek. Postupné načítání umožňuje aplikaci zobrazovat obsah postupně, bez čekání na úplné načtení všech dat.
Hlavní body
Stránkování (z angl. pagination — dělení na stránky) — technika rozdělení velké sady dat na postupné části (stránky). V mobilních aplikacích se stránkování používá při načítání seznamů zpráv, zpravodajských kanálů, katalogů produktů, historie objednávek a jakýchkoli jiných kolekcí s potenciálně neomezeným počtem záznamů.
Bez stránkování je aplikace nucena načíst všechna data najednou, což vede k dlouhému čekání, vysoké spotřebě přenosu a nestabilnímu provozu na slabších zařízeních. Požadavek API se stránkováním vrací pouze jednu část dat a metainformace pro načtení další — tím aplikace kontroluje objem přijímaných informací.
Hlavní metriky stránkování: velikost stránky (page size) — počet záznamů na jedné stránce (obvykle 10-50), a číslo stránky nebo kurzor — ukazatel na aktuální pozici v sadě. Výběr velikosti stránky závisí na typu dat: pro kompaktní prvky (názvy) stačí 20-30, pro karty s obrázky — 10-15.
Mobilní zařízení mají omezené zdroje: množství RAM, rychlost procesoru a limity přenosu. Stránkování řeší tři klíčové úkoly: snižení spotřeby paměti (v paměti jsou uloženy pouze viditelné prvky), zrychlení prvního zobrazení (první část se načte rychleji než celá sada) a úspora přenosu (data se načítají pouze když uživatel posouvá seznam).
Existují čtyři hlavní typy stránkování, z nichž každý řeší specifické úkoly. Výběr metody závisí na požadavcích na konzistenci dat, architektuře API, typu úložiště a povolené složitosti implementace na klientovi a serveru.
| Typ | Princip fungování | Stabilita | Rychlost při velkých objemech |
|---|---|---|---|
| Offset | LIMIT + OFFSET v SQL | nízká | klesá s růstem OFFSET |
| Cursor | WHERE id > last_id | vysoká | stabilní (O(log n)) |
| Keyset | WHERE key > last_key | vysoká | stabilní (O(log n)) |
| Time-based | WHERE created_at < last_time | střední | stabilní s indexem |
Offset stránkování je vhodné pro statické nebo zřídka aktualizované sady dat, když je důležitá jednoduchá implementace. Cursor a Keyset — pro dynamická data s častým vkládáním. Time-based — pro chronologické kanály, kde jsou záznamy seřazeny podle času vytvoření. Standard GraphQL Relay používá cursor stránkování jako jedinou doporučenou metodu.
Offset stránkování — nejjednodušší typ postupného načítání. Klient předává parametry page a limit (nebo offset a limit), server aplikuje SQL OFFSET a LIMIT. Například page=2, limit=20 vrátí záznamy 21 až 40. Tato metoda je intuitivně srozumitelná a snadno implementovatelná na libovolném technologickém stacku.
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
}
Hlavní nevýhoda Offset stránkování — problém vynechaných a duplicitních záznamů. Pokud jsou mezi dvěma požadavky do tabulky přidány nové záznamy, OFFSET se posune: uživatel může vidět stejný záznam dvakrát nebo nový záznam přeskočit. To je kritické pro zpravodajské kanály a chaty, kde je konzistence důležitá.
Další problém — pokles výkonu při velkých OFFSET. Databáze musí skenovat a přeskočit prvních offset záznamů před vrácením výsledku. Při offset=100000 i s LIMIT 20 stráví server znatelný čas skenováním. PostgreSQL a MySQL vykazují lineární pokles rychlosti s růstem OFFSET.
Offset stránkování zůstává nejlepší volbou pro: administrativní panely (data se mění zřídka, je potřeba navigace mezi stránkami), sestavy a historické protokoly (pevný výřez dat), katalogy s filtrováním (lze přejít na libovolnou stránku). Offset je také nejsnáze implementovatelný na klientovi — RecyclerView s Paging 3 jej podporuje ihned.
Keyset stránkování používá unikátní klíč (obvykle primární klíč) pro filtrování záznamů. Místo OFFSET požadavek používá WHERE id > last_seen_id. To zajišťuje stabilní výkon bez ohledu na počet záznamů a absenci duplicit při vkládání, protože nové záznamy mají vždy větší id.
-- Offset-paginace (problematická)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;
-- Keyset-paginace (stabilní)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;
Time-based stránkování (nebo časový kurzor) používá časové razítko created_at pro navigaci. Klient předává timestamp posledního načteného záznamu, server vrací záznamy vytvořené před nebo po tomto razítku. Metoda je populární v sociálních sítích a zpravodajských kanálech, kde pořadí záznamů určuje čas publikace.
Zvláštností Time-based stránkování — možné duplicity, pokud byly dva záznamy vytvořeny ve stejné milisekundě. K odstranění tohoto problému se kombinuje time-based klíč s unikátním id: WHERE (created_at, id) < (last_time, last_id). Takový složený kurzor zaručuje jedinečnost každého záznamu a přesné pořadí.
Keyset stránkování vyžaduje sloupec s unikátní a monotónně rostoucí hodnotou (auto-inkrement id, UUID v7). Time-based je vhodné pro libovolnou tabulku s created_at, ale vyžaduje dodatečné zpracování duplicit. Hlavní rozdíl: Keyset funguje stabilně při jakýchkoli operacích vkládání, zatímco Time-based je citlivé na identická časová razítka.
Výběr typu stránkování závisí na povaze dat a požadavcích na uživatelský zážitek. Níže jsou uvedena doporučení pro typické scénáře v mobilním vývoji. Univerzální řešení neexistuje — každá metoda má oblast použití, ve které je optimální.
Knihovna Android Paging 3 podporuje všechny typy stránkování přes PagingSource. Pro Offset — PagingSource s klíčem Int (page), pro Cursor — s klíčem String nebo Long (cursor). PagingSource automaticky řídí načítání, ukládání do mezipaměti a opakované pokusy při chybách.
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
}
}
}
Velikost stránky ovlivňuje rychlost načítání a vnímání výkonu. Pro mobilní aplikace je optimální rozsah 10-25 prvků na stránku. Méně než 10 — příliš časté požadavky na API a trhané scrollování. Více než 25 — dlouhé načítání první části na pomalých sítích.
Pro obrázky a video se velikost stránky snižuje na 5-10, protože každý prvek vyžaduje další čas pro načtení médií. Pro seznamy s textovými prvky (komentáře, protokoly) lze velikost zvýšit na 30-50 záznamů. Doporučuje se, aby velikost stránky byla konfigurovatelná přes API, aby se klient mohl přizpůsobit různým síťovým podmínkám.
Často kladené otázky
Stránkování — načítání dat po částech, ne všech najednou. Jako v knize: přečtete jednu stránku, pak otočíte na další. V aplikaci to znamená, že při posouvání seznamu se načítá další část dat, ne celý seznam najednou, což šetří přenos a paměť.
Offset počítá záznamy: „přeskoč 20, vrať následujících 10„. Pokud se mezi načítáními přidá nový záznam — číslování se posune. Cursor používá unikátní identifikátor posledního záznamu: „vrať 10 záznamů po ID = 100„. Nové záznamy neovlivňují pozici.
Pro mobilní aplikace je optimálních 10-25 prvků. Pro seznamy s obrázky — 5-10, pro textové kanály — 20-30. Velikost závisí na průměrné velikosti jednoho prvku: čím těžší prvek, tím menší by stránka měla být pro rychlé zobrazení.
Použijte knihovnu Paging 3 z Android Jetpack. Poskytuje PagingSource pro načítání, PagingData pro reaktivní proud a PagingDataAdapter pro automatické načítání při posouvání. Knihovna podporuje Offset, Cursor a Keyset stránkování pomocí vlastního PagingSource.
Nekonečné scrollování — UI vzor, při kterém se nová část dat automaticky načítá při přiblížení ke konci seznamu. Stránkování — je mechanismus načítání dat po částech, a nekonečné scrollování je jeden ze způsobů jeho zobrazení. Alternativou je tlačítko „Načíst více„.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také