Lazy Loading en desarrollo móvil — cómo funciona, principios e implementación

Autor: IT Sectr Publicado: 2026-04-01 Tiempo de lectura: 9 min

Lazy Loading es una estrategia de carga diferida de datos, imágenes y componentes, en la que los recursos no se solicitan al iniciar la aplicación, sino en el momento en que el usuario realmente los necesita. Según la Guía de Android Paging 3, la carga diferida de listas reduce el consumo de memoria entre un 60 y un 80 % al trabajar con grandes conjuntos de datos. La inicialización diferida es el principio clave que subyace a todas las implementaciones de Lazy Loading.

Puntos clave

  • Lazy Loading es un patrón de carga diferida que ahorra memoria y acelera el primer inicio
  • LazyVStack y LazyHStack son componentes integrados de SwiftUI para listas diferidas
  • Paging 3 es una biblioteca de Android para carga paginada de datos desde API y base de datos
  • Bibliotecas de imágenes (Glide, Coil, Kingfisher) cargan imágenes solo cuando aparecen en pantalla
  • Precarga es la carga anticipada, la contrapartida de Lazy Loading para una UX fluida

Qué es Lazy Loading

Lazy Loading (carga diferida o perezosa) es un patrón de diseño y optimización en el que los recursos de la aplicación no se cargan al inicio, sino inmediatamente antes de su uso. En el desarrollo móvil, Lazy Loading se aplica a tres categorías principales: datos (paginación de listas), imágenes (carga al hacer scroll) y componentes (pilas y vistas diferidas).

Lo opuesto a Lazy Loading es Eager Loading (carga anticipada), donde todos los recursos se cargan al iniciar la pantalla. Eager Loading es más simple de implementar, pero consume más memoria y aumenta el tiempo de primera visualización. Para listas con miles de elementos, Eager Loading provoca OOM (Out of Memory) en dispositivos con memoria limitada. Lazy Loading resuelve este problema cargando solo lo que es visible en pantalla y cargando el resto a medida que el usuario desplaza la vista.

En el contexto de iOS y Android, Lazy Loading se implementa en diferentes niveles. SwiftUI proporciona LazyVStack y LazyHStack para renderizado diferido. UIKit utiliza UITableView con dequeueReusableCell. Android utiliza RecyclerView con un pool de ViewHolder. A nivel de datos, Room con Paging 3 y Core Data con NSFetchedResultsController. La elección de la tecnología específica depende de la pila tecnológica y los requisitos de rendimiento.

Principios del funcionamiento de la carga diferida

El principio central de Lazy Loading es cargar exactamente la cantidad de datos necesaria para el estado actual de la pantalla, más un búfer anticipado para un desplazamiento fluido. Este enfoque se basa en dos mecanismos: seguimiento de visibilidad y virtualización de elementos.

Seguimiento de visibilidad

El mecanismo de seguimiento determina qué elementos están en el área visible de la pantalla (viewport). En Android, esto lo hacen LinearLayoutManager o GridLayoutManager mediante los métodos findFirstVisibleItemPosition y findLastVisibleItemPosition. En iOS, UIScrollView proporciona bounds.origin.y y contentOffset.height para calcular el área visible. Cuando un elemento entra en el viewport (o en el búfer de precarga), se inicia su carga. Cuando un elemento sale de la pantalla, sus recursos pueden liberarse o moverse a la caché.

kotlin
// Android: seguimiento de visibilidad en 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
        // Cargando la siguiente página si quedan < 5 elementos
        if (lastVisible >= totalCount - 5) {
            loadMoreItems()
        }
    }
})

Almacenamiento en búfer y precarga

El búfer de precarga es la carga anticipada de elementos que pronto aparecerán en pantalla. RecyclerView admite GapWorker.Prefetch mediante layoutManager.setItemPrefetchEnabled(true). iOS UITableView admite la precarga a través de UITableViewDataSourcePrefetching. El tamaño del búfer de precarga suele ser de 1 a 2 pantallas por delante, lo que proporciona un compromiso entre la fluidez del desplazamiento y el consumo de memoria. Un búfer de precarga demasiado grande anula las ventajas de Lazy Loading; uno demasiado pequeño crea espacios en blanco durante el desplazamiento rápido.

Lazy Loading de imágenes

Las imágenes son el tipo de recurso más pesado en las aplicaciones móviles. Una sola foto de 12 MP puede ocupar de 3 a 5 MB sin comprimir. Lazy Loading de imágenes evita cargar cientos de imágenes invisibles en la memoria, lo que sería fatal para listas con avatares de usuarios o catálogos de productos.

Bibliotecas para Android: Glide y Coil

Glide es la biblioteca de carga de imágenes más popular para Android, compatible con caché, transformaciones y animaciones. Coil es una alternativa más ligera, escrita en Kotlin con corrutinas. Ambas bibliotecas pausan automáticamente la carga cuando un ImageView sale de la pantalla y cancelan las solicitudes cuando se reutiliza un ViewHolder. Coil utiliza corrutinas y tiene un tamaño de ~1.5 MB frente a ~4 MB de Glide, lo que lo hace preferible para proyectos centrados en el tamaño del APK.

kotlin
// Coil: carga diferida de imágenes
imageView.load("https://example.com/image.jpg") {
    crossfade(true)
    placeholder(R.drawable.placeholder)
    size(512, 512)
    memoryCachePolicy(CachePolicy.ENABLED)
}

Bibliotecas para iOS: Kingfisher y SDWebImage

Kingfisher es una biblioteca para iOS compatible con Swift Concurrency, almacenamiento en caché en disco y memoria, y precarga para UICollectionView. SDWebImage es una biblioteca más antigua con raíces en Objective-C pero compatible con Swift. Ambas bibliotecas se integran con UIImageView y gestionan automáticamente el ciclo de vida de la carga: cancelan solicitudes cuando se reutiliza una celda, cargan imágenes solo cuando la celda es visible y liberan memoria ante un aviso de memoria insuficiente.

Lazy Loading de datos y listas

Las listas grandes de datos son el principal ámbito de aplicación de Lazy Loading en aplicaciones móviles. Fuentes de noticias, catálogos de productos, chats, historial de operaciones: cualquier pantalla con una lista potencialmente infinita requiere paginación y carga diferida.

Paging 3 para Android

Paging 3 es una biblioteca de Android Jetpack que implementa el ciclo completo de carga diferida: solicitud de datos desde RemoteMediator (API + base de datos), almacenamiento en caché en Room, salida página por página a través de PagingData y visualización mediante AsyncPagingDataAdapter. Paging 3 admite tres tipos de paginación: basada en páginas, basada en elementos (offset/límite) y basada en claves (claves de paginación de la API). Separator es el soporte integrado para separadores entre páginas para indicadores de carga.

kotlin
// Paging 3: carga diferida desde 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 y LazyHStack

LazyVStack es un contenedor integrado de SwiftUI que crea y renderiza elementos solo cuando aparecen en pantalla. A diferencia de VStack, que calcula inmediatamente el diseño de todos los elementos secundarios, LazyVStack pospone la creación de la vista hasta que el elemento se vuelve visible o entra en el rango de precarga. LazyHStack es el equivalente horizontal para carruseles. Para listas grandes, Apple recomienda usar List, que internamente funciona de manera similar a LazyVStack con reciclaje integrado adicional.

swift
// SwiftUI: LazyVStack con carga diferida
ScrollView {
    LazyVStack(spacing: 8) {
        ForEach(articles) { article in
            ArticleRow(article: article)
                .onAppear {
                    if article == articles.last {
                        viewModel.loadMore()
                    }
                }
        }
    }
}

Lazy Loading de componentes de UI

La carga diferida de componentes de UI es una técnica en la que partes de la interfaz (encabezados, pies de página, secciones de configuración, pestañas) no se crean al iniciar la pantalla, sino en el primer acceso. Esto acelera el renderizado inicial y reduce la carga en el hilo principal.

Android: ViewStub y carga diferida de Fragment

ViewStub es un marcador de posición de View ligero en Android que no ocupa espacio en el diseño y no crea vistas secundarias hasta que se llama a inflate(). Es ideal para secciones de uso poco frecuente: panel de búsqueda, configuración avanzada, bloques de anuncios. La carga diferida de Fragment es una técnica en la que Fragment.onCreateView se pospone hasta que el usuario cambia a esa pestaña. Se implementa mediante isVisible o UserVisibleHint en ViewPager.

AndroidX proporciona SplitInstallManager para la carga diferida de módulos bajo demanda. Los módulos de configuración, diagnóstico o funciones adicionales se cargan como módulos Dynamic Feature solo ante la primera solicitud del usuario. Esto reduce el tamaño base de la aplicación entre un 30 y un 50 % e implementa simultáneamente el principio de Lazy Loading no solo a nivel de datos, sino también a nivel de código.

iOS: TabView y la naturaleza diferida de ViewBuilder

TabView en SwiftUI carga el contenido de cada pestaña de forma diferida, solo cuando se activa la pestaña. UIKit UITabBarController crea todos los controladores secundarios al inicio de forma predeterminada, pero este comportamiento se puede cambiar al no agregarlos a tabBarController.viewControllers inmediatamente y sustituirlos a medida que el usuario cambia. UIStackView con arrangedSubviews agregados dinámicamente también sigue el principio de Lazy Loading: agregue una Subview solo cuando el usuario realice una acción que requiera esa parte de la interfaz.

Para optimizar la carga general de la pantalla, combine Lazy Loading en todos los niveles: ViewStub para secciones de uso poco frecuente, Paging 3 para datos, Glide/Coil para imágenes e inicialización diferida de ViewModel mediante Hilt/Dagger Scopes o Swinject. Este enfoque produce una pantalla que se carga en 200–400 ms incluso en dispositivos económicos con 3 GB de RAM.

Preguntas frecuentes

¿Cuándo no se debe usar Lazy Loading?

Si la pantalla muestra garantizadamente pocos elementos (hasta 20) y todos se necesitan de inmediato, Lazy Loading es excesivo. Para listas que rara vez se desplazan, Eager Loading puede ser más simple y rápido de implementar sin pérdida notable de rendimiento.

¿Cómo afecta Lazy Loading a la memoria?

Reduce el consumo máximo de memoria entre 3 y 10 veces para listas grandes, ya que solo se almacenan en memoria los elementos visibles más el búfer de precarga. Sin embargo, agregar precarga y almacenamiento en caché de imágenes crea un gasto de memoria moderado que debe gestionarse.

¿Qué elegir: LazyVStack o List en SwiftUI?

List es preferible para datos homogéneos con soporte de deslizamiento, arrastrar y soltar y selección integrada. LazyVStack es para diseños personalizados con diferentes tipos de celdas, secciones y espacios no estándar. List internamente funciona como LazyVStack con funcionalidad adicional.

¿Cómo depurar problemas de carga diferida?

En Android, use el Layout Inspector para ver la jerarquía de vistas: con Lazy Loading, la mayoría de los elementos deberían estar ausentes del árbol. En iOS, use Xcode Debug View Hierarchy. Si todos los elementos están presentes durante el desplazamiento, Lazy Loading no funciona.

¿Qué es más importante: la velocidad de carga o el ahorro de memoria?

Ambos parámetros son importantes, pero la prioridad depende de la plataforma. En iOS con ARC y gestión eficiente de memoria, la prioridad es la velocidad de carga. En Android con JVM y GC, la prioridad es el ahorro de memoria, ya que cada asignación en el hilo principal puede causar una congelación del GC.

Resumen

  • Lazy Loading es un patrón de carga diferida que acelera el primer inicio y ahorra memoria
  • Las imágenes se cargan mediante Glide, Coil, Kingfisher solo cuando entran en el viewport
  • Paging 3 para Android proporciona carga paginada de datos con almacenamiento en caché en Room
  • LazyVStack en SwiftUI pospone el renderizado de elementos hasta que aparecen en pantalla
  • ViewStub en Android: carga diferida de componentes de UI en el primer acceso
  • Búfer de precarga: carga anticipada para un desplazamiento fluido
  • Combine Lazy Loading en todos los niveles: datos + imágenes + UI

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también