Lazy Loading — məlumatların, şəkillərin və komponentlərin gecikmiş yüklənməsi strategiyasıdır, burada resurslar tətbiqin başlanğıcında deyil, istifadəçinin onlara həqiqətən ehtiyacı olduğu anda sorğulanır. Android Paging 3 Guide-a görə, siyahıların tənbəl yüklənməsi böyük verilənlər dəstləri ilə işləyərkən yaddaş istehlakını 60–80% azaldır. Gecikmiş başlatma — bütün Lazy Loading tətbiqlərinin əsasında duran əsas prinsipdir.
Əsas məqamlar
Lazy Loading (tənbəl yükləmə, gecikmiş yükləmə) — tətbiq resurslarının başlanğıcda deyil, istifadədən əvvəl birbaşa yükləndiyi dizayn və optimallaşdırma nümunəsidir. Mobil inkişafda Lazy Loading üç əsas kateqoriyaya tətbiq olunur: məlumatlar (siyahıların paginasiyası), şəkillər (sürüşdürmə zamanı yükləmə) və komponentlər (tənbəl steklər və görünüşlər).
Lazy Loading-in əksi Eager Loading (acgöz yükləmə) adlanır, burada bütün resurslar ekranın başlanğıcında yüklənir. Eager Loading tətbiqi daha sadədir, lakin daha çox yaddaş sərf edir və ilk göstərmə vaxtını artırır. Minlərlə elementi olan siyahılar üçün Eager Loading məhdud yaddaşlı cihazlarda OOM-a (Out of Memory) səbəb olur. Lazy Loading bu problemi həll edir, yalnız ekranda görünənləri yükləyir və qalanını sürüşdürmə zamanı əlavə edir.
iOS və Android kontekstində Lazy Loading müxtəlif səviyyələrdə tətbiq olunur. SwiftUI tənbəl render üçün LazyVStack və LazyHStack təmin edir. UIKit dequeueReusableCell ilə UITableView-dən istifadə edir. Android — ViewHolder hovuzu ilə RecyclerView. Məlumat səviyyəsində — Paging 3 ilə Room və NSFetchedResultsController ilə Core Data. Konkret texnologiyanın seçimi stack-dən və performans tələblərindən asılıdır.
Mərkəzi prinsip Lazy Loading — ekranın cari vəziyyəti üçün lazım olan dəqiq məlumat həcmini üstəgəl hamar sürüşdürmə üçün qabaqcadan yükləmə buferini yükləməkdir. Bu yanaşma iki mexanizmə əsaslanır: görünmənin izlənməsi və elementlərin virtualizasiyası.
İzləmə mexanizmi ekranın görünən sahəsində (viewport) hansı elementlərin olduğunu müəyyən edir. Android-də bunu LinearLayoutManager və ya GridLayoutManager findFirstVisibleItemPosition və findLastVisibleItemPosition metodları vasitəsilə edir. iOS-da UIScrollView bounds.origin.y və contentOffset.height ilə görünən sahəni hesablamağı təmin edir. Element viewport-a (və ya prefetch buferinə) daxil olduqda onun yüklənməsi başlayır. Element ekranı tərk etdikdə onun resursları sərbəst buraxıla və ya keşə köçürülə bilər.
// Android — RecyclerView-da görünmənin izlənməsi
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
// Növbəti səhifəni yükləyirik, əgər 5 elementdən az qalıbsa < 5 element
if (lastVisible >= totalCount - 5) {
loadMoreItems()
}
}
})
Prefetch buferi — tezliklə ekranda görünəcək elementlərin qabaqcadan yüklənməsidir. RecyclerView layoutManager.setItemPrefetchEnabled(true) vasitəsilə GapWorker.Prefetch-i dəstəkləyir. iOS UITableView UITableViewDataSourcePrefetching vasitəsilə prefetching-i dəstəkləyir. Prefetch buferinin ölçüsü adətən 1–2 ekran qabağadır ki, bu da sürüşdürmənin hamarlığı ilə yaddaş istehlakı arasında kompromis təmin edir. Çox böyük prefetch buferi Lazy Loading-in üstünlüklərini itirir, çox kiçik — sürətli sürüşdürmədə boş yerlər yaradır.
Şəkillər — mobil tətbiqlərdə ən ağır resurs növüdür. 12 MP-lik bir foto sıxılmamış formada 3–5 MB yer tuta bilər. Şəkillərin Lazy Loading-i yüzlərlə görünməz şəklin yaddaşa yüklənməsinin qarşısını alır ki, bu da istifadəçi şərhləri siyahıları və ya məhsul kataloqları üçün fəlakətli olardı.
Glide — keşləmə, transformasiya və animasiya dəstəyi ilə Android üçün ən populyar şəkil yükləmə kitabxanasıdır. Coil — Kotlin dilində korutinlərlə yazılmış daha yüngül alternativdir. Hər iki kitabxana ImageView ekranı tərk etdikdə yükləməni avtomatik dayandırır və ViewHolder təkrar istifadə edildikdə sorğuları ləğv edir. Coil korutinlərdən istifadə edir və ~1.5 MB ölçüsü ilə (~4 MB olan Glide-ə qarşı) APK ölçüsünə önəm verən layihələr üçün üstünlük təşkil edir.
// Coil — şəklin tənbəl yüklənməsi
imageView.load("https://example.com/image.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
size(512, 512)
memoryCachePolicy(CachePolicy.ENABLED)
}
Kingfisher — Swift Concurrency, disk və yaddaş keşləməsi, həmçinin UICollectionView üçün prefetching dəstəyi ilə iOS kitabxanasıdır. SDWebImage — Objective-C kökləri olan, lakin Swift dəstəyi ilə daha köhnə kitabxanadır. Hər iki kitabxana UIImageView ilə inteqrasiya olunur və yükləmənin həyat dövrünü avtomatik idarə edir: hüceyrə təkrar istifadə edildikdə sorğuları ləğv edir, şəkilləri yalnız hüceyrə görünəndə yükləyir və yaddaş çatışmazlığı xəbərdarlığı zamanı yaddaşı boşaldır.
Böyük məlumat siyahıları — mobil tətbiqlərdə Lazy Loading-in əsas tətbiq sahəsidir. Xəbər lenti, məhsul kataloqu, söhbətlər, əməliyyat tarixçəsi — potensial olaraq sonsuz siyahısı olan hər ekran paginasiya və gecikmiş yükləmə tələb edir.
Paging 3 — Android Jetpack-dən tənbəl yükləmənin tam dövrünü həyata keçirən kitabxana: RemoteMediator-dan məlumat sorğusu (API + verilənlər bazası), Room-da keşləmə, PagingData vasitəsilə səhifəli çatdırılma və AsyncPagingDataAdapter vasitəsilə göstərmə. Paging 3 üç növ paginasiyanı dəstəkləyir: Page-based (səhifələr), Item-based (offset/limit) və Key-based (API-dən paginasiya açarları). Separator — yükləmə göstəriciləri üçün səhifələr arasında daxili ayırıcı dəstəyi.
// Paging 3 — API-dan tənbəl yükləmə
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 — SwiftUI-nin daxili konteyneri, elementləri yalnız ekranda görünəndə yaradır və render edir. Bütün uşaq elementlərin layout-unu dərhal hesablayan VStack-dən fərqli olaraq, LazyVStack görünüşün yaradılmasını element görünən olana və ya prefetch diapazonuna daxil olana qədər təxirə salır. LazyHStack — karusellər üçün üfüqi analoq. Böyük həcmli siyahılar üçün Apple daxili recyc lingu ilə LazyVStack kimi işləyən List-dən istifadə etməyi tövsiyə edir.
// SwiftUI — LazyVStack tənbəl yükləmə ilə
ScrollView {
LazyVStack(spacing: 8) {
ForEach(articles) { article in
ArticleRow(article: article)
.onAppear {
if article == articles.last {
viewModel.loadMore()
}
}
}
}
}
UI komponentlərinin tənbəl yüklənməsi — interfeys hissələrinin (başlıqlar, alt bilgilər, tənzimləmə bölmələri, tablar) ekranın başlanğıcında deyil, onlara ilk müraciət zamanı yaradıldığı texnikadır. Bu, ilkin renderi sürətləndirir və main thread yükünü azaldır.
ViewStub — Android-də yüngül View doldurucusudur, layout-da yer tutmur və inflate() çağırılana qədər uşaq View-lər yaratmır. Nadir istifadə olunan bölmələr üçün idealdır: axtarış paneli, genişləndirilmiş tənzimləmələr, reklam blokları. Fragment tənbəl yükləməsi — Fragment.onCreateView-in istifadəçi həmin taba keçənə qədər təxirə salındığı texnikadır. ViewPager-də isVisible və ya UserVisibleHint vasitəsilə həyata keçirilir.
AndroidX tələb üzrə modulların tənbəl yüklənməsi üçün SplitInstallManager təmin edir. Tənzimləmə, diaqnostika və ya əlavə funksiyalar modulları yalnız istifadəçinin ilk sorğusunda Dynamic Feature modulları kimi yüklənir. Bu, tətbiqin əsas ölçüsünü 30–50% azaldır və eyni zamanda prinsipi təkcə məlumat səviyyəsində deyil, həm də kod səviyyəsində həyata keçirir.
TabView SwiftUI-də hər tabın məzmununu tənbəl şəkildə — yalnız tab aktivləşdirildikdə yükləyir. UIKit UITabBarController standart olaraq başlanğıcda bütün uşaq kontrollerləri yaradır, lakin bu davranışı onları dərhal tabBarController.viewControllers-a əlavə etməməklə, keçid zamanı əlavə etməklə dəyişmək olar. Dinamik olaraq əlavə edilən arrangedSubviews ilə UIStackView də Lazy Loading prinsipinə əməl edir — Subview-i yalnız istifadəçi interfeysin həmin hissəsini tələb edən əməliyyatı yerinə yetirdikdə əlavə edin.
Ekranın yüklənməsini optimallaşdırmaq üçün Lazy Loading-i bütün səviyyələrdə birləşdirin: nadir istifadə olunan bölmələr üçün ViewStub, məlumatlar üçün Paging 3, şəkillər üçün Glide/Coil və Hilt/Dagger Scopes və ya Swinject vasitəsilə ViewModel-in tənbəl başlatması. Bu yanaşma hətta 3 GB operativ yaddaşı olan büdcə cihazlarında belə 200–400 ms ərzində yüklənən ekran verir.
Tez-tez verilən suallar
Ekran az sayda element (20-yə qədər) göstərirsə və hamısı dərhal lazımdırsa — Lazy Loading artıqdır. Nadir sürüşdürülən siyahılar üçün Eager Loading nəzərə çarpan performans itkisi olmadan daha sadə və sürətli ola bilər.
Böyük siyahılar üçün pik yaddaş istehlakını 3–10 dəfə azaldır, çünki yaddaşda yalnız görünən elementlər üstəgəl prefetch buferi saxlanılır. Bununla belə, prefetch və şəkil keşləməsinin əlavə edilməsi idarə edilməli olan mülayim yaddaş sərfi yaradır.
List sürüşdürmə, sürükləmə və daxili seçim imkanı olan homojen məlumatlar üçün üstünlük təşkil edir. LazyVStack — müxtəlif hüceyrə tipləri, bölmələr və qeyri-standart boşluqlarla fərdi layoutlar üçün. List daxili olaraq LazyVStack kimi işləyir, əlavə funksionallıqla.
Android-də View iyerarxiyasına baxmaq üçün Layout Inspector-dan istifadə edin: Lazy Loading zamanı elementlərin çoxu ağacda olmamalıdır. iOS-da — Xcode Debug View Hierarchy. Sürüşdürmə zamanı bütün elementlər mövcuddursa — Lazy Loading işləmir.
Hər iki parametr vacibdir, lakin prioritet platformadan asılıdır. ARC və effektiv yaddaş idarəçiliyi ilə iOS-da prioritet yükləmə sürətidir. JVM və GC ilə Android-də prioritet yaddaş qənaətidir, çünki main thread-da hər alokasiya GC-frizə səbəb ola bilər.
Xülasə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun