Paginatie — een techniek voor het pagina voor pagina laden van gegevens, gebruikt in mobiele applicaties en webservices voor het werken met grote datasets. Volgens Android Developers Documentation (2025) vermindert een correcte implementatie van paginatie de belasting van de API, bespaart het verkeer en verbetert het de gebruikerservaring. Pagina-voor-pagina laden stelt de app in staat om inhoud geleidelijk weer te geven, zonder te wachten op het volledig laden van alle gegevens.
Belangrijkste punten
Paginatie (van Engels pagination — paginaindeling) — een techniek om een grote dataset op te delen in opeenvolgende porties (pagina's). In mobiele apps wordt paginatie gebruikt bij het laden van berichtenlijsten, nieuwsfeeds, productcatalogi, bestelgeschiedenis en alle andere verzamelingen met een potentieel onbeperkt aantal records.
Zonder paginatie moet de app alle gegevens in één keer laden, wat leidt tot lange wachttijden, hoog verbruik van verkeer en instabiele prestaties op zwakkere apparaten. API-verzoek met paginatie retourneert slechts één portie gegevens en meta-informatie voor het laden van de volgende — zo controleert de app de hoeveelheid ontvangen informatie.
De belangrijkste metrieken van paginatie: paginagrootte (page size) — het aantal records op één pagina (meestal 10-50), en paginanummer of cursor — een aanwijzer naar de huidige positie in de dataset. De keuze van paginagrootte hangt af van het gegevenstype: voor compacte elementen (namen) is 20-30 voldoende, voor kaarten met afbeeldingen — 10-15.
Mobiele apparaten hebben beperkte bronnen: hoeveelheid RAM, processorsnelheid en verkeerslimieten. Paginatie lost drie belangrijke taken op: vermindering van geheugengebruik (alleen zichtbare elementen worden in het geheugen bewaard), versnelling van de eerste weergave (de eerste portie laadt sneller dan de volledige set) en besparing van verkeer (gegevens worden alleen geladen wanneer de gebruiker door de lijst scrollt).
Er zijn vier hoofdtypen paginatie, elk met specifieke taken. De keuze van de methode hangt af van de vereisten voor gegevensconsistentie, API-architectuur, opslagtype en de toegestane complexiteit van implementatie aan client- en serverzijde.
| Type | Werkingsprincipe | Stabiliteit | Snelheid bij grote volumes |
|---|---|---|---|
| Offset | LIMIT + OFFSET in SQL | laag | daalt met groei van OFFSET |
| Cursor | WHERE id > last_id | hoog | stabiel (O(log n)) |
| Keyset | WHERE key > last_key | hoog | stabiel (O(log n)) |
| Time-based | WHERE created_at < last_time | gemiddeld | stabiel met index |
Offset-paginatie is geschikt voor statische of zelden bijgewerkte datasets, wanneer eenvoudige implementatie belangrijk is. Cursor en Keyset — voor dynamische gegevens met frequente toevoegingen. Time-based — voor chronologische feeds, waar records zijn geordend op aanmaaktijd. GraphQL-standaard Relay gebruikt cursor-paginatie als de enige aanbevolen methode.
Offset-paginatie — het eenvoudigste type paginagewijs laden. De client stuurt parameters page en limit (of offset en limit), de server past SQL OFFSET en LIMIT toe. Bijvoorbeeld page=2, limit=20 retourneert records 21 tot 40. Deze methode is intuïtief begrijpelijk en gemakkelijk te implementeren op elke technische stack.
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
}
Het grootste nadeel van Offset-paginatie — probleem van gemiste en dubbele records. Als er tussen twee verzoeken nieuwe records aan de tabel worden toegevoegd, verschuift de OFFSET: de gebruiker kan een record twee keer zien of een nieuwe missen. Dit is kritisch voor nieuwsfeeds en chats waar consistentie belangrijk is.
Een ander probleem — prestatieverlies bij grote OFFSET-waarden. De database moet de eerste offset records scannen en overslaan voordat het resultaat wordt geretourneerd. Bij offset=100000 zelfs met LIMIT 20 besteedt de server merkbare tijd aan scannen. PostgreSQL en MySQL vertonen een lineaire snelheidsdaling naarmate OFFSET toeneemt.
Offset-paginatie blijft de beste keuze voor: beheerpanelen (gegevens veranderen zelden, navigatie tussen pagina's nodig), rapporten en historische logs (vaste gegevenssnede), catalogi met filtering (kan naar elke pagina navigeren). Offset is ook het eenvoudigst te implementeren aan clientzijde — RecyclerView met Paging 3 ondersteunt het uit de doos.
Keyset-paginatie gebruikt een unieke sleutel (meestal de primaire sleutel) om records te filteren. In plaats van OFFSET gebruikt het verzoek WHERE id > last_seen_id. Dit zorgt voor stabiele prestaties ongeacht het aantal records en geen duplicaten bij toevoegingen, omdat nieuwe records altijd een grotere id hebben.
-- Offset-paginering (problematisch)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;
-- Keyset-paginering (stabiel)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;
Time-based paginatie (of tijdcursor) gebruikt de tijdstempel created_at voor navigatie. De client stuurt de timestamp van het laatst geladen record, de server retourneert records die voor of na deze tijdstempel zijn gemaakt. De methode is populair in sociale netwerken en nieuwsfeeds, waar de volgorde van records wordt bepaald door de publicatietijd.
Een kenmerk van Time-based paginatie — mogelijke duplicaten als twee records in dezelfde milliseconde zijn gemaakt. Om dit probleem op te lossen wordt time-based sleutel gecombineerd met unieke id: WHERE (created_at, id) < (last_time, last_id). Zo'n samengestelde cursor garandeert de uniciteit van elk record en de exacte volgorde.
Keyset-paginatie vereist een kolom met een unieke en monotoon stijgende waarde (auto-increment id, UUID v7). Time-based is geschikt voor elke tabel met created_at, maar vereist extra verwerking van duplicaten. Het belangrijkste verschil: Keyset werkt stabiel bij elke invoeging, terwijl Time-based gevoelig is voor identieke tijdstempels.
De keuze van het paginatietype hangt af van de aard van de gegevens en de vereisten voor de gebruikerservaring. Hieronder staan aanbevelingen voor typische scenario's in mobiele ontwikkeling. Er is geen universele oplossing — elke methode heeft een toepassingsgebied waarin deze optimaal is.
De bibliotheek Android Paging 3 ondersteunt alle paginatietypen via PagingSource. Voor Offset — PagingSource met Int-sleutel (page), voor Cursor — met String of Long-sleutel (cursor). PagingSource beheert automatisch laden, cachen en opnieuw proberen bij fouten.
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
}
}
}
De paginagrootte beïnvloedt de laadsnelheid en de perceptie van prestaties. Voor mobiele apps is het optimale bereik 10-25 elementen per pagina. Minder dan 10 — te frequente verzoeken aan de API en schokkerig scrollen. Meer dan 25 — lange laadtijd van de eerste portie op trage netwerken.
Voor afbeeldingen en video wordt de paginagrootte verlaagd naar 5-10, omdat elk element extra tijd nodig heeft voor het laden van media. Voor lijsten met tekstuele elementen (reacties, logs) kan de grootte worden verhoogd naar 30-50 records. Het wordt aanbevolen om de paginagrootte configureerbaar te maken via de API, zodat de client zich kan aanpassen aan verschillende netwerkomstandigheden.
Veelgestelde vragen
Paginatie — het laden van gegevens in porties, niet alles in één keer. Zoals in een boek: je leest een pagina, dan ga je naar de volgende. In een app betekent dit dat tijdens het scrollen van de lijst de volgende portie gegevens wordt geladen, niet de hele lijst in één keer, wat verkeer en geheugen bespaart.
Offset telt records: „sla 20 over, retourneer de volgende 10„. Als er tussen laadbeurten een nieuw record wordt toegevoegd — raakt de nummering van slag. Cursor gebruikt een unieke identificatie van het laatste record: „retourneer 10 records na ID = 100„. Nieuwe records hebben geen invloed op de positie.
Voor mobiele apps is 10-25 elementen optimaal. Voor lijsten met afbeeldingen — 5-10, voor tekstfeeds — 20-30. De grootte hangt af van de gemiddelde grootte van één element: hoe zwaarder het element, hoe kleiner de pagina moet zijn voor snelle weergave.
Gebruik de bibliotheek Paging 3 van Android Jetpack. Deze biedt PagingSource voor laden, PagingData voor reactieve stroom en PagingDataAdapter voor automatisch laden tijdens scrollen. De bibliotheek ondersteunt Offset-, Cursor- en Keyset-paginatie via aangepaste PagingSource.
Oneindig scrollen — een UI-patroon waarbij een nieuwe portie gegevens automatisch wordt geladen bij het naderen van het einde van de lijst. Paginatie — is het mechanisme van het laden van gegevens in porties, en oneindig scrollen is een van de manieren om het weer te geven. Het alternatief is de knop „Laad meer„.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook