Lazy Loading у мобилном развоју — како функционише, принципи и имплементација

Аутор: IT Sectr Објављено: 2026-04-01 Време читања: 9 мин

Lazy Loading — стратегија одложеног учитавања података, слика и компоненти, при чему се ресурси захтевају не при покретању апликације, већ у тренутку када су кориснику стварно потребни. Према Android Paging 3 Guide, лења загрузка листи смањује потрошњу меморије за 60–80% при раду са великим скуповима података. Одложена иницијализација — кључни принцип који лежи у основи свих имплементација Lazy Loading-а.

Главне ствари

  • Lazy Loading — образац одложеног учитавања који штеди меморију и убрзава прво покретање
  • LazyVStack и LazyHStack — уграђене SwiftUI компоненте за лење листе
  • Paging 3 — Android библиотека за странично учитавање података из API-ја и базе података
  • Библиотеке за слике (Glide, Coil, Kingfisher) учитавају слике тек када се појаве на екрану
  • Preloading — претходно учитавање, друга страна Lazy Loading-а за гладак UX

Шта је Lazy Loading

Lazy Loading (лења загрузка, одложено учитавање) — образац пројектовања и оптимизације у којем се ресурси апликације учитавају не при покретању, већ непосредно пре употребе. У мобилном развоју, Lazy Loading се примењује на три главне категорије: подаци (пагинација листи), слике (учитавање при скроловању) и компоненте (лењи стекови и прикази).

Супротност Lazy Loading-у је Eager Loading (похлепно учитавање), када се сви ресурси учитавају при покретању екрана. Eager Loading је једноставнији за имплементацију, али троши више меморије и повећава време првог приказивања. За листе са хиљадама елемената, Eager Loading доводи до OOM (Out of Memory) на уређајима са ограниченом меморијом. Lazy Loading решава овај проблем учитавајући само оно што је видљиво на екрану и додајући остало током скроловања.

У контексту iOS и Android-а, Lazy Loading је имплементиран на различитим нивоима. SwiftUI пружа LazyVStack и LazyHStack за лење рендеровање. UIKit користи UITableView са dequeueReusableCell. Android — RecyclerView са ViewHolder пулом. На нивоу података — Room са Paging 3 и Core Data са NSFetchedResultsController. Избор одређене технологије зависи од стека и захтева за перформансама.

Принципи рада лење загрузке

Централни принцип Lazy Loading-а — учитавање тачно оне количине података која је потребна за тренутно стање екрана, плус бафер за претходно учитавање за глатко скроловање. Овај приступ се заснива на два механизма: праћење видљивости и виртуелизација елемената.

Праћење видљивости

Механизам праћења одређује који елементи се налазе у видљивом подручју екрана (viewport). На Android-у ово ради LinearLayoutManager или GridLayoutManager преко метода findFirstVisibleItemPosition и findLastVisibleItemPosition. У iOS-у, UIScrollView обезбеђује bounds.origin.y и contentOffset.height за израчунавање видљивог подручја. Када елемент уђе у viewport (или у prefetch бафер), покреће се његово учитавање. Када елемент напусти екран, његови ресурси могу бити ослобођени или премештени у кеш.

kotlin
// Android — праћење видљивости у 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
        // Учитавамо следећу страницу ако је остало мање од 5 елемената < 5 элементов
        if (lastVisible >= totalCount - 5) {
            loadMoreItems()
        }
    }
})

Баферизација и prefetch

Prefetch бафер — претходно учитавање елемената који ће се ускоро појавити на екрану. RecyclerView подржава GapWorker.Prefetch преко layoutManager.setItemPrefetchEnabled(true). iOS UITableView подржава prefetching преко UITableViewDataSourcePrefetching. Величина prefetch бафера је обично 1–2 екрана унапред, што даје компромис између глаткоће скроловања и потрошње меморије. Превелик prefetch бафер поништава предности Lazy Loading-а, премали — ствара празна места при брзом скроловању.

Lazy Loading слика

Слике — најтежи тип ресурса у мобилним апликацијама. Једна фотографија од 12 MP може заузимати 3–5 MB у некомпримованом облику. Lazy Loading слика спречава учитавање стотина невидљивих слика у меморију, што би било фатално за листе корисничких коментара или каталоге производа.

Библиотеке за Android: Glide и Coil

Glide — најпопуларнија библиотека за учитавање слика за Android са подршком за кеширање, трансформације и анимације. Coil — лакша алтернатива написана у Kotlin-у коришћењем корутина. Обе библиотеке аутоматски заустављају учитавање када ImageView напусти екран и поништавају захтеве при поновном коришћењу ViewHolder-а. Coil користи корутине и величине је ~1.5 MB наспрам ~4 MB код Glide-а, што га чини пожељнијим за пројекте фокусиране на величину APK-а.

kotlin
// Coil — лења загрузка слике
imageView.load("https://example.com/image.jpg") {
    crossfade(true)
    placeholder(R.drawable.placeholder)
    size(512, 512)
    memoryCachePolicy(CachePolicy.ENABLED)
}

Библиотеке за iOS: Kingfisher и SDWebImage

Kingfisher — библиотека за iOS са подршком за Swift Concurrency, кеширање на диску и у меморији, као и prefetching за UICollectionView. SDWebImage — старија библиотека са Objective-C коренима, али са подршком за Swift. Обе библиотеке се интегришу са UIImageView и аутоматски управљају животним циклусом учитавања: поништавају захтеве при поновном коришћењу ћелије, учитавају слике само када је ћелија видљива и ослобађају меморију при обавештењу о недостатку меморије.

Lazy Loading података и листи

Велике листе података — главно подручје примене Lazy Loading-а у мобилним апликацијама. Фид вести, каталог производа, четови, историја операција — сваки екран са потенцијално бесконачном листом захтева пагинацију и одложено учитавање.

Paging 3 за Android

Paging 3 — библиотека из Android Jetpack-а која имплементира пуни циклус лење загрузке: захтевање података из RemoteMediator-а (API + база података), кеширање у Room-у, странично издавање преко PagingData и приказ преко AsyncPagingDataAdapter-а. Paging 3 подржава три типа пагинације: Page-based (странице), Item-based (offset/limit) и Key-based (кључеви пагинације из API-ја). Separator — уграђена подршка за раздвајаче између страница за индикаторе учитавања.

kotlin
// Paging 3 — лења загрузка из 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)
        }
    }
}

SwiftUI LazyVStack и LazyHStack

LazyVStack — уграђени SwiftUI контејнер који креира и рендерује елементе тек када се појаве на екрану. За разлику од VStack-а који одмах израчунава layout свих подређених елемената, LazyVStack одлаже креирање приказа до тренутка када елемент постане видљив или уђе у prefetch опсег. LazyHStack — хоризонтални аналог за каруселе. За листе великог обима, Apple препоручује коришћење List-а који унутрашњости ради слично LazyVStack-у са додатним уграђеним рециклирањем.

swift
// SwiftUI — LazyVStack са лењом загрузком
ScrollView {
    LazyVStack(spacing: 8) {
        ForEach(articles) { article in
            ArticleRow(article: article)
                .onAppear {
                    if article == articles.last {
                        viewModel.loadMore()
                    }
                }
        }
    }
}

Lazy Loading UI компоненти

Лења загрузка UI компоненти — техника у којој се делови интерфејса (заглавља, подножја, секције подешавања, картице) креирају не при покретању екрана, већ при првом приступу њима. Ово убрзава почетно рендеровање и смањује оптерећење на main thread-у.

Android: ViewStub и лења загрузка Fragment-а

ViewStub — лагани View попунилац у Android-у који не заузима место у layout-у и не креира подређене View-ове до позива inflate(). Идеалан за ретко коришћене секције: панел за претрагу, напредна подешавања, рекламни блокови. Лења загрузка Fragment-а — техника у којој се Fragment.onCreateView одлаже до тренутка када корисник пређе на ту картицу. Имплементира се кроз isVisible или UserVisibleHint у ViewPager-у.

AndroidX обезбеђује SplitInstallManager за лењу загрузку модула на захтев. Модули подешавања, дијагностике или додатних функција учитавају се као Dynamic Feature модули тек при првом захтеву корисника. Ово смањује основну величину апликације за 30–50% и истовремено имплементира принцип Lazy Loading-а не само на нивоу података, већ и кода.

iOS: TabView и лењост ViewBuilder-а

TabView у SwiftUI-ју учитава садржај сваке картице лењо — тек при активацији картице. UIKit UITabBarController подразумевано креира све подређене контролере при покретању, али ово понашање се може променити њиховим недодавањем у tabBarController.viewControllers одмах, већ додавањем при пребацивању. UIStackView са динамички додатим arrangedSubviews такође следи принцип Lazy Loading-а — додајте Subview само када корисник изврши радњу која захтева тај део интерфејса.

За оптимизацију учитавања екрана у целини, комбинујте Lazy Loading на свим нивоима: ViewStub за ретко коришћене секције, Paging 3 за податке, Glide/Coil за слике и лења иницијализација ViewModel-а кроз Hilt/Dagger Scopes или Swinject. Овакав приступ даје екран који се учитава за 200–400 ms чак и на буџетским уређајима са 3 GB RAM-а.

Често постављана питања

Када не треба користити Lazy Loading?

Ако екран гарантовано приказује мало елемената (до 20) и сви су потребни одмах — Lazy Loading је сувишан. За листе које се ретко скролују, Eager Loading може бити једноставнији и бржи за имплементацију без приметног губитка перформанси.

Како Lazy Loading утиче на меморију?

Смањује вршну потрошњу меморије 3–10 пута за велике листе, јер се у меморији чувају само видљиви елементи плус prefetch бафер. Међутим, додавање prefetch-а и кеширање слика ствара умерену потрошњу меморије којом треба управљати.

Шта изабрати — LazyVStack или List у SwiftUI-ју?

List је пожељнији за хомогене податке са могућношћу превлачења, претапања и уграђене селекције. LazyVStack — за прилагођене layout-е са различитим типовима ћелија, секцијама и нестандардним размацима. List унутрашњости ради као LazyVStack са додатном функционалношћу.

Како отклонити проблеме са лењом загрузком?

У Android-у користите Layout Inspector за преглед хијерархије View-ова: при Lazy Loading-у већина елемената треба да буде одсутна из стабла. У iOS-у — Xcode Debug View Hierarchy. Ако су при скроловању сви елементи присутни — Lazy Loading не ради.

Шта је важније — брзина учитавања или уштеда меморије?

Оба параметра су важна, али приоритет зависи од платформе. На iOS-у са ARC-ом и ефикасним управљањем меморијом приоритет је брзина учитавања. На Android-у са JVM и GC-ом приоритет је уштеда меморије, јер свака алокација у main thread-у може изазвати GC-фриз.

Резиме

  • Lazy Loading — образац одложеног учитавања који убрзава прво покретање и штеди меморију
  • Слике се учитавају кроз Glide, Coil, Kingfisher тек при уласку у viewport
  • Paging 3 за Android обезбеђује странично учитавање података са кеширањем у Room-у
  • LazyVStack у SwiftUI-ју одлаже рендеровање елемената до појављивања на екрану
  • ViewStub у Android-у — лења загрузка UI компоненти при првом приступу
  • Prefetch бафер — претходно учитавање за глатко скроловање
  • Комбинујте Lazy Loading на свим нивоима: подаци + слике + UI

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође