Lazy Loading — een strategie voor het vertraagd laden van gegevens, afbeeldingen en componenten, waarbij bronnen niet bij het opstarten van de app worden opgevraagd, maar op het moment dat de gebruiker ze echt nodig heeft. Volgens Android Paging 3 Guide vermindert lui laden van lijsten het geheugengebruik met 60–80% bij het werken met grote datasets. Uitgestelde initialisatie — het kernprincipe dat ten grondslag ligt aan alle Lazy Loading-implementaties.
Belangrijkste punten
Lazy Loading (lui laden, vertraagd laden) — een ontwerp- en optimalisatiepatroon waarbij app-bronnen niet bij het opstarten worden geladen, maar direct voor gebruik. In mobiele ontwikkeling wordt Lazy Loading toegepast op drie hoofdcategorieën: gegevens (paginering van lijsten), afbeeldingen (laden tijdens scrollen) en componenten (luie stacks en views).
Het tegenovergestelde van Lazy Loading is Eager Loading (gretig laden), waarbij alle bronnen worden geladen bij het starten van het scherm. Eager Loading is eenvoudiger te implementeren, maar verbruikt meer geheugen en verlengt de tijd van de eerste weergave. Voor lijsten met duizenden elementen leidt Eager Loading tot OOM (Out of Memory) op apparaten met beperkt geheugen. Lazy Loading lost dit probleem op door alleen te laden wat zichtbaar is op het scherm en de rest toe te voegen tijdens het scrollen.
In de context van iOS en Android is Lazy Loading op verschillende niveaus geïmplementeerd. SwiftUI biedt LazyVStack en LazyHStack voor lui renderen. UIKit gebruikt UITableView met dequeueReusableCell. Android — RecyclerView met ViewHolder-pool. Op dataniveau — Room met Paging 3 en Core Data met NSFetchedResultsController. De keuze van specifieke technologie hangt af van de stack en prestatie-eisen.
Het centrale principe van Lazy Loading — het laden van precies de hoeveelheid gegevens die nodig is voor de huidige schermtoestand, plus een vooruit buffer voor vloeiend scrollen. Deze aanpak is gebaseerd op twee mechanismen: zichtbaarheidsregistratie en virtualisatie van elementen.
Het registratiemechanisme bepaalt welke elementen zich in het zichtbare gebied van het scherm (viewport) bevinden. Op Android doet LinearLayoutManager of GridLayoutManager dit via de methoden findFirstVisibleItemPosition en findLastVisibleItemPosition. In iOS biedt UIScrollView bounds.origin.y en contentOffset.height voor het berekenen van het zichtbare gebied. Wanneer een element de viewport (of prefetch buffer) binnenkomt, wordt het laden gestart. Wanneer het element het scherm verlaat, kunnen de bronnen worden vrijgegeven of naar de cache worden verplaatst.
// Android — zichtbaarheidsregistratie in RecyclerView
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val lastVisible = layoutManager.findLastVisibleItemPosition()
val totalCount = layoutManager.itemCount
// We laden de volgende pagina als er minder dan 5 elementen over zijn < 5 elementen
if (lastVisible >= totalCount - 5) {
loadMoreItems()
}
}
})
Prefetch buffer — het vooruit laden van elementen die binnenkort op het scherm verschijnen. RecyclerView ondersteunt GapWorker.Prefetch via layoutManager.setItemPrefetchEnabled(true). iOS UITableView ondersteunt prefetching via UITableViewDataSourcePrefetching. De grootte van de prefetch buffer is meestal 1–2 schermen vooruit, wat een compromis biedt tussen scrollvloeiendheid en geheugengebruik. Te grote prefetch buffer teniet de voordelen van Lazy Loading, te kleine — creëert lege plekken bij snel scrollen.
Afbeeldingen — het zwaarste type bronnen in mobiele apps. Eén foto van 12 MP kan 3–5 MB in beslag nemen in ongecomprimeerde vorm. Lazy Loading van afbeeldingen voorkomt dat honderden onzichtbare afbeeldingen in het geheugen worden geladen, wat fataal zou zijn voor lijsten met gebruikersreacties of productcatalogi.
Glide — de populairste bibliotheek voor het laden van afbeeldingen voor Android met ondersteuning voor caching, transformaties en animaties. Coil — een lichter alternatief geschreven in Kotlin met behulp van coroutines. Beide bibliotheken stoppen automatisch met laden wanneer ImageView het scherm verlaat en annuleren verzoeken bij hergebruik van ViewHolder. Coil gebruikt coroutines en heeft een grootte van ~1.5 MB versus ~4 MB van Glide, waardoor het de voorkeur heeft voor projecten die gericht zijn op APK-grootte.
// Coil — lui laden van afbeelding
imageView.load("https://example.com/image.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
size(512, 512)
memoryCachePolicy(CachePolicy.ENABLED)
}
Kingfisher — bibliotheek voor iOS met ondersteuning voor Swift Concurrency, caching op schijf en in geheugen, en prefetching voor UICollectionView. SDWebImage — een oudere bibliotheek met Objective-C wortels maar met ondersteuning voor Swift. Beide bibliotheken integreren met UIImageView en beheren automatisch de levenscyclus van het laden: annuleren verzoeken bij hergebruik van de cel, laden afbeeldingen alleen wanneer de cel zichtbaar is en geven geheugen vrij bij melding van geheugengebrek.
Grote lijsten met gegevens — het belangrijkste toepassingsgebied van Lazy Loading in mobiele apps. Nieuwsfeed, productcatalogus, chats, transactiegeschiedenis — elk scherm met een potentieel oneindige lijst vereist paginering en vertraagd laden.
Paging 3 — bibliotheek uit Android Jetpack die de volledige cyclus van lui laden implementeert: gegevens opvragen van RemoteMediator (API + database), cachen in Room, paginagewijs leveren via PagingData en weergeven via AsyncPagingDataAdapter. Paging 3 ondersteunt drie typen paginering: Page-based (pagina's), Item-based (offset/limit) en Key-based (paginering-sleutels van API). Separator — ingebouwde ondersteuning voor scheidingstekens tussen pagina's voor laadindicatoren.
// Paging 3 — lui laden vanuit API
class ArticlePagingSource(
private val api: ArticleApi
) : PagingSource<Int, Article>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, Article> {
return try {
val page = params.key ?: 1
val response = api.getArticles(page)
LoadResult.Page(
data = response.items,
prevKey = page.takeIf { it > 1 }?.dec(),
nextKey = page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
LazyVStack — ingebouwde SwiftUI-container die elementen pas maakt en rendert wanneer ze op het scherm verschijnen. In tegenstelling tot VStack, die onmiddellijk de layout van alle onderliggende elementen berekent, stelt LazyVStack het aanmaken van de view uit tot het element zichtbaar wordt of in het prefetch-bereik komt. LazyHStack — de horizontale tegenhanger voor carrousels. Voor lijsten met grote volumes raadt Apple aan om List te gebruiken, die intern werkt als LazyVStack met extra ingebouwde recycling.
// SwiftUI — LazyVStack met lui laden
ScrollView {
LazyVStack(spacing: 8) {
ForEach(articles) { article in
ArticleRow(article: article)
.onAppear {
if article == articles.last {
viewModel.loadMore()
}
}
}
}
}
Lui laden van UI-componenten — techniek waarbij delen van de interface (headers, footers, instellingensecties, tabbladen) niet bij het starten van het scherm worden gemaakt, maar bij de eerste toegang ertoe. Dit versnelt de initiële render en vermindert de belasting van de main thread.
ViewStub — een lichte View-placeholder in Android die geen ruimte inneemt in de layout en geen onderliggende Views maakt tot de aanroep van inflate(). Ideaal voor zelden gebruikte secties: zoekpaneel, geavanceerde instellingen, advertentieblokken. Lui laden van Fragment — techniek waarbij Fragment.onCreateView wordt uitgesteld tot de gebruiker naar dat tabblad schakelt. Geïmplementeerd via isVisible of UserVisibleHint in ViewPager.
AndroidX biedt SplitInstallManager voor het op aanvraag laden van modules. Modules voor instellingen, diagnostiek of extra functies worden alleen bij de eerste gebruikersaanvraag als Dynamic Feature-modules geladen. Dit verkleint de basisgrootte van de app met 30–50% en implementeert tegelijkertijd het principe van Lazy Loading niet alleen op dataniveau, maar ook op codeniveau.
TabView in SwiftUI laadt de inhoud van elk tabblad lui — alleen bij activering van het tabblad. UIKit UITabBarController maakt standaard alle onderliggende controllers bij het opstarten, maar dit gedrag kan worden gewijzigd door ze niet meteen aan tabBarController.viewControllers toe te voegen, maar toe te voegen tijdens het schakelen. UIStackView met dynamisch toegevoegde arrangedSubviews volgt ook het principe van Lazy Loading — voeg Subview alleen toe wanneer de gebruiker een actie uitvoert dat dat deel van de interface vereist.
Voor optimalisatie van het laden van het scherm als geheel, combineer Lazy Loading op alle niveaus: ViewStub voor zelden gebruikte secties, Paging 3 voor gegevens, Glide/Coil voor afbeeldingen en luie initialisatie van ViewModel via Hilt/Dagger Scopes of Swinject. Een dergelijke aanpak geeft een scherm dat in 200–400 ms laadt, zelfs op budgetapparaten met 3 GB RAM.
Veelgestelde vragen
Als het scherm gegarandeerd weinig elementen (tot 20) toont en ze allemaal direct nodig zijn — is Lazy Loading overbodig. Voor lijsten die zelden worden gescrolld, kan Eager Loading eenvoudiger en sneller te implementeren zijn zonder merkbaar prestatieverlies.
Vermindert het piekgeheugengebruik met 3–10 keer voor grote lijsten, omdat alleen zichtbare elementen plus de prefetch buffer in het geheugen worden opgeslagen. Het toevoegen van prefetch en het cachen van afbeeldingen creëert echter een matig geheugengebruik dat moet worden beheerd.
List heeft de voorkeur voor homogene gegevens met swipe, sleep en ingebouwde selectie. LazyVStack — voor aangepaste layouts met verschillende celtypen, secties en niet-standaard tussenruimtes. List werkt intern als LazyVStack met extra functionaliteit.
Gebruik in Android Layout Inspector om de View-hiërarchie te bekijken: bij Lazy Loading zouden de meeste elementen in de boom moeten ontbreken. In iOS — Xcode Debug View Hierarchy. Als bij scrollen alle elementen aanwezig zijn — werkt Lazy Loading niet.
Beide parameters zijn belangrijk, maar de prioriteit hangt af van het platform. Op iOS met ARC en efficiënt geheugenbeheer is laadsnelheid de prioriteit. Op Android met JVM en GC is geheugenbesparing de prioriteit, omdat elke allocatie in de main thread GC-freeze kan veroorzaken.
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