Lazy Loading, kaynakların uygulama başlangıcında değil, kullanıcının gerçekten ihtiyaç duyduğu anda talep edildiği bir veri, görüntü ve bileşen gecikmeli yükleme stratejisidir. Android Paging 3 Rehberi'ne göre, listelerin gecikmeli yüklenmesi, büyük veri kümeleriyle çalışırken bellek tüketimini %60–80 oranında azaltır. Gecikmeli başlatma, tüm Lazy Loading uygulamalarının temelindeki anahtar ilkedir.
Önemli Noktalar
Lazy Loading (gecikmeli yükleme, tembel yükleme), uygulama kaynaklarının başlangıçta değil, kullanımdan hemen önce yüklendiği bir tasarım ve optimizasyon desenidir. Mobil geliştirmede Lazy Loading üç ana kategoride uygulanır: veri (liste sayfalama), görseller (kaydırma sırasında yükleme) ve bileşenler (gecikmeli yığınlar ve görünümler).
Lazy Loading'in tersi, tüm kaynakların ekran başlangıcında yüklendiği Eager Loading (istekli yükleme)'dir. Eager Loading uygulaması daha basittir ancak daha fazla bellek tüketir ve ilk görüntüleme süresini artırır. Binlerce öğeye sahip listeler için Eager Loading, sınırlı belleğe sahip cihazlarda OOM (bellek yetersizliği) sorununa yol açar. Lazy Loading, yalnızca ekranda görüneni yükleyerek ve geri kalanını kullanıcı kaydırdıkça yükleyerek bu sorunu çözer.
iOS ve Android bağlamında, Lazy Loading farklı seviyelerde uygulanır. SwiftUI, gecikmeli işleme için LazyVStack ve LazyHStack sağlar. UIKit, dequeueReusableCell ile UITableView kullanır. Android, ViewHolder havuzu ile RecyclerView kullanır. Veri seviyesinde, Paging 3 ile Room ve NSFetchedResultsController ile Core Data bulunur. Belirli teknolojinin seçimi yığına ve performans gereksinimlerine bağlıdır.
Merkezi ilke, mevcut ekran durumu için tam olarak ihtiyaç duyulan veri miktarını ve akıcı kaydırma için tahmine dayalı bir arabelleği yüklemektir. Bu yaklaşım iki mekanizmaya dayanır: görünürlük izleme ve öğe sanallaştırma.
İzleme mekanizması, hangi öğelerin ekranın görünür alanında (viewport) olduğunu belirler. Android'de bunu LinearLayoutManager veya GridLayoutManager, findFirstVisibleItemPosition ve findLastVisibleItemPosition yöntemleri aracılığıyla yapar. iOS'ta UIScrollView, görünür alanı hesaplamak için bounds.origin.y ve contentOffset.height sağlar. Bir öğe viewport'a (veya ön getirme arabelleğine) girdiğinde yüklemesi başlatılır. Bir öğe ekrandan ayrıldığında, kaynakları serbest bırakılabilir veya önbelleğe taşınabilir.
// Android — RecyclerView'de görünürlük izleme
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
// Kalan varsa sonraki sayfa yükleniyor < 5 öğe
if (lastVisible >= totalCount - 5) {
loadMoreItems()
}
}
})
Ön getirme arabelleği, yakında ekranda görünecek öğelerin tahmine dayalı yüklenmesidir. RecyclerView, layoutManager.setItemPrefetchEnabled(true) aracılığıyla GapWorker.Prefetch'i destekler. iOS UITableView, UITableViewDataSourcePrefetching aracılığıyla ön getirmeyi destekler. Ön getirme arabelleğinin boyutu tipik olarak 1–2 ekran ileridedir ve kaydırma akıcılığı ile bellek tüketimi arasında bir uzlaşma sağlar. Çok büyük bir ön getirme arabelleği Lazy Loading'in avantajlarını geçersiz kılar; çok küçük bir arabellek, hızlı kaydırma sırasında boş alanlar oluşturur.
Görseller, mobil uygulamalardaki en ağır kaynak türüdür. 12 MP'lik tek bir fotoğraf, sıkıştırılmamış biçimde 3–5 MB yer kaplayabilir. Görsellerin Lazy Loading'i, yüzlerce görünmeyen görselin belleğe yüklenmesini önler; bu, kullanıcı avatarları veya ürün katalogları içeren listeler için ölümcül olurdu.
Glide, önbellekleme, dönüştürme ve animasyonları destekleyen Android için en popüler görsel yükleme kütüphanesidir. Coil, coroutine'ler kullanılarak Kotlin'de yazılmış daha hafif bir alternatiftir. Her iki kütüphane de bir ImageView ekrandan ayrıldığında otomatik olarak yüklemeyi duraklatır ve bir ViewHolder yeniden kullanıldığında istekleri iptal eder. Coil, coroutine'leri kullanır ve Glide'in ~4 MB'ına kıyasla ~1,5 MB boyutundadır, bu da onu APK boyutuna odaklanan projeler için tercih edilir kılar.
// Coil — gecikmeli görsel yükleme
imageView.load("https://example.com/image.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
size(512, 512)
memoryCachePolicy(CachePolicy.ENABLED)
}
Kingfisher, Swift Concurrency, disk ve bellek önbellekleme ve UICollectionView için ön getirme desteği sunan bir iOS kütüphanesidir. SDWebImage, Objective-C köklerine sahip ancak Swift desteği olan daha eski bir kütüphanedir. Her iki kütüphane de UIImageView ile entegre olur ve yükleme yaşam döngüsünü otomatik olarak yönetir: bir hücre yeniden kullanıldığında istekleri iptal eder, görselleri yalnızca hücre görünür olduğunda yükler ve düşük bellek uyarısı bildiriminde belleği serbest bırakır.
Büyük veri listeleri, mobil uygulamalarda Lazy Loading'in ana uygulama alanıdır. Haber akışı, ürün kataloğu, sohbetler, işlem geçmişi — potansiyel olarak sonsuz listeye sahip her ekran, sayfalama ve gecikmeli yükleme gerektirir.
Paging 3, Android Jetpack'ten gelen ve tam gecikmeli yükleme döngüsünü uygulayan bir kütüphanedir: RemoteMediator'dan (API + veritabanı) veri talebi, Room'da önbellekleme, PagingData aracılığıyla sayfa sayfa çıktı ve AsyncPagingDataAdapter aracılığıyla görüntüleme. Paging 3 üç tür sayfalama destekler: sayfa tabanlı, öğe tabanlı (offset/limit) ve anahtar tabanlı (API'den sayfalama anahtarları). Separator, yükleme göstergeleri için sayfalar arasında ayırıcılar için yerleşik destektir.
// Paging 3 — API'den gecikmeli yükleme
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, öğeleri yalnızca ekranda göründüklerinde oluşturan ve işleyen yerleşik bir SwiftUI kapsayıcısıdır. Tüm alt öğelerin düzenini hemen hesaplayan VStack'in aksine, LazyVStack, öğe görünür hale gelene veya ön getirme aralığına girene kadar görünüm oluşturmayı erteler. LazyHStack, atlıkarıncalar için yatay eşdeğerdir. Büyük listeler için Apple, dahili olarak ek yerleşik geri dönüşümle LazyVStack gibi çalışan List'in kullanılmasını önerir.
// SwiftUI — gecikmeli yüklemeli LazyVStack
ScrollView {
LazyVStack(spacing: 8) {
ForEach(articles) { article in
ArticleRow(article: article)
.onAppear {
if article == articles.last {
viewModel.loadMore()
}
}
}
}
}
UI bileşenlerinin gecikmeli yüklenmesi, arayüzün bazı bölümlerinin (başlıklar, alt bilgiler, ayar bölümleri, sekmeler) ekran başlangıcında değil, ilk erişimde oluşturulduğu bir tekniktir. Bu, ilk işlemeyi hızlandırır ve ana iş parçacığındaki yükü azaltır.
ViewStub, Android'de düzende yer kaplamayan ve inflate() çağrılana kadar alt View'lar oluşturmayan hafif bir View yer tutucusudur. Nadiren kullanılan bölümler için idealdir: arama paneli, gelişmiş ayarlar, reklam blokları. Fragment gecikmeli yükleme, kullanıcı o sekmeye geçene kadar Fragment.onCreateView'in ertelendiği bir tekniktir. ViewPager'da isVisible veya UserVisibleHint aracılığıyla uygulanır.
AndroidX, isteğe bağlı modül yüklemesi için SplitInstallManager sağlar. Kurulum, teşhis veya ek özellik modülleri, yalnızca kullanıcının ilk talebinde Dynamic Feature modülleri olarak yüklenir. Bu, temel uygulama boyutunu %30–50 oranında azaltır ve aynı anda Lazy Loading'in ilkesini yalnızca veri seviyesinde değil, kod seviyesinde de uygular.
TabView, SwiftUI'de her sekmenin içeriğini gecikmeli olarak yükler — yalnızca sekme etkinleştirildiğinde. UIKit UITabBarController, varsayılan olarak başlangıçta tüm alt denetleyicileri oluşturur, ancak bu davranış, bunları hemen tabBarController.viewControllers'a eklemeyerek ve kullanıcı geçiş yaptıkça değiştirerek değiştirilebilir. Dinamik olarak eklenen arrangedSubviews ile UIStackView de Lazy Loading ilkesini izler — yalnızca kullanıcı arayüzün o bölümünü gerektiren bir eylem gerçekleştirdiğinde bir Subview ekleyin.
Genel ekran yüklemeyi optimize etmek için Lazy Loading'i tüm seviyelerde birleştirin: nadiren kullanılan bölümler için ViewStub, veriler için Paging 3, görseller için Glide/Coil ve Hilt/Dagger Scopes veya Swinject aracılığıyla gecikmeli ViewModel başlatma. Bu yaklaşım, 3 GB RAM'e sahip bütçe cihazlarda bile 200–400 ms'de yüklenen bir ekran sağlar.
Sıkça Sorulan Sorular
Ekran garantili olarak az sayıda öğe (20'ye kadar) gösteriyorsa ve tümü hemen gerekiyorsa, Lazy Loading gereksizdir. Nadiren kaydırılan listeler için Eager Loading, gözle görülür bir performans kaybı olmadan uygulanması daha basit ve daha hızlı olabilir.
Büyük listeler için tepe bellek tüketimini 3–10 kat azaltır, çünkü bellekte yalnızca görünür öğeler artı ön getirme arabelleği depolanır. Ancak, ön getirme ve görsel önbelleği eklemek, yönetilmesi gereken orta düzeyde bir bellek yükü oluşturur.
List, kaydırma, sürükle-bırak ve yerleşik seçim desteğiyle homojen veriler için tercih edilir. LazyVStack, farklı hücre türleri, bölümler ve standart olmayan boşluklarla özel düzenler içindir. List, dahili olarak ek işlevsellikle LazyVStack gibi çalışır.
Android'de, View hiyerarşisini görmek için Layout Inspector kullanın: Lazy Loading ile öğelerin çoğu ağaçta bulunmamalıdır. iOS'ta, Xcode Debug View Hierarchy kullanın. Kaydırma sırasında tüm öğeler mevcutsa, Lazy Loading çalışmıyordur.
Her iki parametre de önemlidir, ancak öncelik platforma bağlıdır. ARC ve verimli bellek yönetimi ile iOS'ta öncelik yükleme hızıdır. JVM ve GC ile Android'de öncelik bellek tasarrufudur, çünkü ana iş parçacığındaki her ayırma GC donmasına neden olabilir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun