Lazy Loading — strategia de încărcare întârziată a datelor, imaginilor și componentelor, în care resursele sunt solicitate nu la pornirea aplicației, ci în momentul în care utilizatorul are cu adevărat nevoie de ele. Conform Android Paging 3 Guide, încărcarea lentă a listelor reduce consumul de memorie cu 60–80% la lucrul cu seturi mari de date. Inițializarea întârziată — principiul cheie care stă la baza tuturor implementărilor Lazy Loading.
Principalele
Lazy Loading (încărcare lentă, încărcare întârziată) — model de proiectare și optimizare în care resursele aplicației sunt încărcate nu la pornire, ci imediat înainte de utilizare. În dezvoltarea mobilă, Lazy Loading se aplică la trei categorii principale: date (paginarea listelor), imagini (încărcarea la derulare) și componente (stive și vizualizări leneșe).
Opusul Lazy Loading este Eager Loading (încărcare eager), când toate resursele sunt încărcate la pornirea ecranului. Eager Loading este mai simplu de implementat, dar consumă mai multă memorie și crește timpul primei afișări. Pentru liste cu mii de elemente, Eager Loading duce la OOM (Out of Memory) pe dispozitive cu memorie limitată. Lazy Loading rezolvă această problemă încărcând doar ceea ce este vizibil pe ecran și adăugând restul pe măsură ce utilizatorul derulează.
În contextul iOS și Android, Lazy Loading este implementat la diferite niveluri. SwiftUI oferă LazyVStack și LazyHStack pentru randare lentă. UIKit folosește UITableView cu dequeueReusableCell. Android — RecyclerView cu pool-ul de ViewHolder. La nivel de date — Room cu Paging 3 și Core Data cu NSFetchedResultsController. Alegerea tehnologiei specifice depinde de stack și de cerințele de performanță.
Principiul central al Lazy Loading — încărcarea exact a volumului de date necesar pentru starea curentă a ecranului, plus un buffer anticipativ pentru derulare fluentă. Această abordare se bazează pe două mecanisme: urmărirea vizibilității și virtualizarea elementelor.
Mecanismul de urmărire determină care elemente se află în zona vizibilă a ecranului (viewport). Pe Android, acest lucru este realizat de LinearLayoutManager sau GridLayoutManager prin metodele findFirstVisibleItemPosition și findLastVisibleItemPosition. În iOS, UIScrollView oferă bounds.origin.y și contentOffset.height pentru calcularea zonei vizibile. Când un element intră în viewport (sau în bufferul prefetch), se pornește încărcarea sa. Când elementul părăsește ecranul, resursele sale pot fi eliberate sau mutate în cache.
// Android — urmărirea vizibilității în 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
// Încărcăm pagina următoare dacă au mai rămas mai puțin de 5 elemente < 5 elemente
if (lastVisible >= totalCount - 5) {
loadMoreItems()
}
}
})
Bufferul Prefetch — încărcarea anticipată a elementelor care vor apărea în curând pe ecran. RecyclerView suportă GapWorker.Prefetch prin layoutManager.setItemPrefetchEnabled(true). iOS UITableView suportă prefetching prin UITableViewDataSourcePrefetching. Dimensiunea bufferului prefetch este de obicei 1–2 ecrane înainte, oferind un compromis între fluența derulării și consumul de memorie. Prea mare buffer prefetch anulează avantajele Lazy Loading, prea mic — creează spații goale la derularea rapidă.
Imaginile — cel mai greu tip de resurse în aplicațiile mobile. O fotografie de 12 MP poate ocupa 3–5 MB în formă necomprimată. Lazy Loading al imaginilor previne încărcarea a sute de imagini invizibile în memorie, ceea ce ar fi fatal pentru liste cu comentarii utilizatori sau cataloage de produse.
Glide — cea mai populară bibliotecă de încărcare a imaginilor pentru Android cu suport pentru cache, transformări și animații. Coil — o alternativă mai ușoară scrisă în Kotlin folosind corutine. Ambele biblioteci opresc automat încărcarea când ImageView părăsește ecranul și anulează cererile la reutilizarea ViewHolder. Coil folosește corutine și are dimensiunea de ~1.5 MB față de ~4 MB la Glide, ceea ce îl face preferat pentru proiecte focusate pe dimensiunea APK.
// Coil — încărcarea lentă a imaginii
imageView.load("https://example.com/image.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
size(512, 512)
memoryCachePolicy(CachePolicy.ENABLED)
}
Kingfisher — bibliotecă pentru iOS cu suport pentru Swift Concurrency, cache pe disc și în memorie, precum și prefetching pentru UICollectionView. SDWebImage — o bibliotecă mai veche cu rădăcini Objective-C, dar cu suport pentru Swift. Ambele biblioteci se integrează cu UIImageView și gestionează automat ciclul de viață al încărcării: anulează cererile la reutilizarea celulei, încarcă imaginile doar când celula este vizibilă și eliberează memoria la notificarea de lipsă de memorie.
Listele mari de date — principalul domeniu de aplicare al Lazy Loading în aplicațiile mobile. Fluxul de știri, catalogul de produse, chat-urile, istoricul operațiilor — orice ecran cu o listă potențial infinită necesită paginare și încărcare întârziată.
Paging 3 — bibliotecă din Android Jetpack care implementează ciclul complet de încărcare lentă: solicitarea datelor din RemoteMediator (API + bază de date), cache în Room, livrare paginată prin PagingData și afișare prin AsyncPagingDataAdapter. Paging 3 suportă trei tipuri de paginare: Page-based (pagini), Item-based (offset/limit) și Key-based (chei de paginare de la API). Separator — suport încorporat pentru separatoare între pagini pentru indicatorii de încărcare.
// Paging 3 — încărcarea lentă din 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 — containerul încorporat SwiftUI care creează și randază elemente doar la apariția lor pe ecran. Spre deosebire de VStack, care calculează imediat layout-ul tuturor elementelor copil, LazyVStack amână crearea vizualizării până când elementul devine vizibil sau intră în intervalul prefetch. LazyHStack — analogul orizontal pentru carusele. Pentru liste de volum mare, Apple recomandă utilizarea List, care intern funcționează similar cu LazyVStack cu reciclare încorporată suplimentară.
// SwiftUI — LazyVStack cu încărcare lentă
ScrollView {
LazyVStack(spacing: 8) {
ForEach(articles) { article in
ArticleRow(article: article)
.onAppear {
if article == articles.last {
viewModel.loadMore()
}
}
}
}
}
Încărcarea lentă a componentelor UI — tehnica în care părți ale interfeței (header-e, footer-e, secțiuni de setări, file) sunt create nu la pornirea ecranului, ci la prima accesare a acestora. Aceasta accelerează randarea inițială și reduce sarcina pe main thread.
ViewStub — un placeholder ușor View în Android care nu ocupă loc în layout și nu creează View-uri copil până la apelul inflate(). Ideal pentru secțiuni rar utilizate: panoul de căutare, setări avansate, blocuri publicitare. Încărcarea lentă a Fragment — tehnica în care Fragment.onCreateView este amânat până când utilizatorul comută la acea filă. Se implementează prin isVisible sau UserVisibleHint în ViewPager.
AndroidX oferă SplitInstallManager pentru încărcarea lentă a modulelor la cerere. Modulele de setări, diagnosticare sau funcții suplimentare sunt încărcate ca module Dynamic Feature doar la prima solicitare a utilizatorului. Aceasta reduce dimensiunea de bază a aplicației cu 30–50% și implementează simultan principiul Lazy Loading nu doar la nivel de date, ci și la nivel de cod.
TabView în SwiftUI încarcă conținutul fiecărei file lent — doar la activarea filei. UIKit UITabBarController în mod implicit creează toate controlerele copil la pornire, dar acest comportament poate fi modificat prin neadăugarea lor imediată la tabBarController.viewControllers și adăugarea pe măsură ce se comută. UIStackView cu arrangedSubviews adăugate dinamic respectă, de asemenea, principiul Lazy Loading — adăugați Subview doar când utilizatorul efectuează o acțiune care necesită acea parte a interfeței.
Pentru optimizarea încărcării ecranului în ansamblu, combinați Lazy Loading la toate nivelurile: ViewStub pentru secțiuni rar utilizate, Paging 3 pentru date, Glide/Coil pentru imagini și inițializarea lentă a ViewModel prin Hilt/Dagger Scopes sau Swinject. O astfel de abordare oferă un ecran care se încarcă în 200–400 ms chiar și pe dispozitive bugetare cu 3 GB RAM.
Întrebări frecvente
Dacă ecranul arată garantat puține elemente (până la 20) și toate sunt necesare imediat — Lazy Loading este redundant. Pentru liste care sunt rareori derulate, Eager Loading poate fi mai simplu și mai rapid de implementat fără pierderi vizibile de performanță.
Reduce consumul de vârf al memoriei de 3–10 ori pentru liste mari, deoarece în memorie sunt stocate doar elementele vizibile plus bufferul prefetch. Cu toate acestea, adăugarea prefetch și cache-ul imaginilor creează un consum moderat de memorie care trebuie gestionat.
List este preferat pentru date omogene cu posibilitate de swipe, tragere și selecție încorporată. LazyVStack — pentru layout-uri personalizate cu diferite tipuri de celule, secțiuni și spații non-standard. List intern funcționează ca LazyVStack cu funcționalitate suplimentară.
În Android utilizați Layout Inspector pentru vizualizarea ierarhiei View: la Lazy Loading majoritatea elementelor ar trebui să lipsească din arbore. În iOS — Xcode Debug View Hierarchy. Dacă la derulare toate elementele sunt prezente — Lazy Loading nu funcționează.
Ambii parametri sunt importanți, dar prioritatea depinde de platformă. Pe iOS cu ARC și gestionarea eficientă a memoriei, prioritatea este viteza de încărcare. Pe Android cu JVM și GC, prioritatea este economisirea memoriei, deoarece fiecare alocare în main thread poate cauza GC-freeze.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și