Pagina — teknik för sidvis laddning av data, som används i mobila applikationer och webbtjänster för att arbeta med stora datamängder. Enligt Android Developers Documentation (2025) minskar korrekt implementering av pagina belastningen på API:et, sparar trafik och förbättrar användarupplevelsen. Sidvis laddning gör att applikationen kan visa innehåll gradvis, utan att vänta på fullständig laddning av all data.
Huvudpunkter
Pagina (från eng. pagination — sidindelning) — en teknik för att dela upp en stor datamängd i successiva omgångar (sidor). I mobila applikationer används pagina vid laddning av meddelandelistor, nyhetsflöden, produktkataloger, orderhistorik och alla andra samlingar med potentiellt obegränsat antal poster.
Utan pagina tvingas applikationen ladda all data på en gång, vilket leder till lång väntan, hög trafikförbrukning och instabil drift på svaga enheter. API-förfrågan med pagina returnerar endast en omgång data och metainformation för att ladda nästa — på så sätt kontrollerar applikationen mängden mottagen information.
De viktigaste mätvärdena för pagina: sidstorlek (page size) — antal poster på en sida (vanligtvis 10-50), och sidnummer eller markör — en pekare till aktuell position i mängden. Val av sidstorlek beror på datatyp: för kompakta element (namn) räcker 20-30, för kort med bilder — 10-15.
Mobila enheter har begränsade resurser: mängd RAM, processorhastighet och trafikgränser. Pagina löser tre viktiga uppgifter: minskad minnesförbrukning (endast synliga element lagras i minnet), snabbare första visning (första omgången laddas snabbare än hela mängden) och trafikbesparing (data laddas endast när användaren scrollar listan).
Det finns fyra huvudtyper av pagina, var och en löser specifika uppgifter. Valet av metod beror på kraven för datakonsistens, API-arkitektur, lagringstyp och tillåten komplexitet i implementeringen på klient och server.
| Typ | Funktionsprincip | Stabilitet | Hastighet vid stora volymer |
|---|---|---|---|
| Offset | LIMIT + OFFSET i SQL | låg | sjunker med ökande OFFSET |
| Cursor | WHERE id > last_id | hög | stabil (O(log n)) |
| Keyset | WHERE key > last_key | hög | stabil (O(log n)) |
| Time-based | WHERE created_at < last_time | medel | stabil med index |
Offset-pagina passar för statiska eller sällan uppdaterade datamängder, när enkel implementering är viktig. Cursor och Keyset — för dynamisk data med frekventa insättningar. Time-based — för kronologiska flöden, där poster sorteras efter skapelsetid. GraphQL-standarden Relay använder cursor-pagina som den enda rekommenderade metoden.
Offset-pagina — den enklaste typen av sidvis laddning. Klienten skickar parametrar page och limit (eller offset och limit), servern tillämpar SQL OFFSET och LIMIT. Till exempel, page=2, limit=20 returnerar poster 21 till 40. Denna metod är intuitivt förståelig och lätt att implementera på vilken teknisk stack som helst.
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
}
Den största nackdelen med Offset-pagina — problemet med missade och dubbla poster. Om nya poster läggs till i tabellen mellan två förfrågningar, flyttas OFFSET: användaren kan se samma post två gånger eller missa en ny. Detta är kritiskt för nyhetsflöden och chattar där konsistens är viktig.
Ett annat problem — prestandaförsämring vid stora OFFSET. Databasen måste skanna och hoppa över de första offset posterna innan resultatet returneras. Vid offset=100000 även med LIMIT 20 kommer servern att lägga märkbar tid på skanning. PostgreSQL och MySQL uppvisar linjär hastighetsminskning med ökande OFFSET.
Offset-pagina är fortfarande det bästa valet för: administrationspaneler (data ändras sällan, navigering mellan sidor behövs), rapporter och historiska loggar (fast datasektion), kataloger med filtrering (kan navigera till valfri sida). Offset är också enklast att implementera på klientsidan — RecyclerView med Paging 3 stöder det direkt.
Keyset-pagina använder en unik nyckel (vanligtvis primärnyckeln) för att filtrera poster. Istället för OFFSET använder förfrågan WHERE id > last_seen_id. Detta säkerställer stabil prestanda oavsett antal poster och inga dubbletter vid insättningar, eftersom nya poster alltid har större id.
-- Offset-paginering (problematisk)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;
-- Keyset-paginering (stabil)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;
Time-based pagina (eller tidsmarkör) använder tidsstämpeln created_at för navigering. Klienten skickar timestamp för senast laddade posten, servern returnerar poster skapade före eller efter denna stämpel. Metoden är populär i sociala nätverk och nyhetsflöden, där ordningen på poster bestäms av publiceringstiden.
En egenskap hos Time-based pagina — möjliga dubbletter om två poster skapas i samma millisekund. För att eliminera detta problem kombineras time-based nyckel med unikt id: WHERE (created_at, id) < (last_time, last_id). En sådan sammansatt markör garanterar varje posts unikhet och exakt ordning.
Keyset-pagina kräver en kolumn med unikt och monotont ökande värde (auto-inkrement id, UUID v7). Time-based passar för alla tabeller med created_at, men kräver extra bearbetning av dubbletter. Huvudskillnaden: Keyset fungerar stabilt vid alla insättningsoperationer, medan Time-based är känslig för identiska tidsstämplar.
Valet av paginatyp beror på datans natur och kraven på användarupplevelse. Nedan finns rekommendationer för typiska scenarier inom mobil utveckling. Det finns ingen universallösning — varje metod har ett tillämpningsområde där den är optimal.
Biblioteket Android Paging 3 stöder alla paginatyper via PagingSource. För Offset — PagingSource med Int-nyckel (page), för Cursor — med String eller Long-nyckel (cursor). PagingSource hanterar automatiskt laddning, cachning och omförsök vid fel.
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
}
}
}
Sidstorleken påverkar laddningshastigheten och upplevd prestanda. För mobila applikationer är det optimala intervallet 10-25 element per sida. Mindre än 10 — alltför frekventa förfrågningar till API:et och ryckig scroll. Mer än 25 — lång laddning av första omgången på långsamma nätverk.
För bilder och video minskas sidstorleken till 5-10, eftersom varje element kräver extra tid för att ladda media. För listor med textbaserade element (kommentarer, loggar) kan storleken ökas till 30-50 poster. Det rekommenderas att sidstorleken är konfigurerbar via API:et, så att klienten kan anpassa sig till olika nätverksförhållanden.
Vanliga frågor
Pagina — laddning av data i omgångar, inte allt på en gång. Som i en bok: du läser en sida, sedan bläddrar du till nästa. I en app betyder det att när du scrollar listan laddas nästa omgång data, inte hela listan på en gång, vilket sparar trafik och minne.
Offset räknar poster: “hoppa över 20, returnera nästa 10”. Om en ny post läggs till mellan laddningar — förskjuts numreringen. Cursor använder unik identifierare för den senaste posten: ”returnera 10 poster efter ID = 100”. Nya poster påverkar inte positionen.
För mobila applikationer är 10-25 element optimalt. För listor med bilder — 5-10, för textflöden — 20-30. Storleken beror på genomsnittlig storlek för ett element: ju tyngre element, desto mindre bör sidan vara för snabb visning.
Använd biblioteket Paging 3 från Android Jetpack. Det tillhandahåller PagingSource för laddning, PagingData för reaktiv ström och PagingDataAdapter för automatisk laddning vid scrollning. Biblioteket stöder Offset-, Cursor- och Keyset-pagina via anpassad PagingSource.
Oändlig scrollning — ett UI-mönster där en ny omgång data automatiskt laddas när man närmar sig slutet av listan. Pagina — är mekanismen för att ladda data i omgångar, och oändlig scrollning är ett av sätten att visa den. Alternativet är knappen ”Ladda mer”.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också