Estrategia de caching Sprint 2 - jcrinconv/MISW4203-2026-12-ing-sw-apps-moviles GitHub Wiki

Estrategias de Caching - OptimizaciΓ³n de Memoria

1. Estrategia: CachΓ© en Memoria (CacheManager)

DΓ³nde se usa

CacheManager es un Singleton thread-safe (@Volatile + synchronized) ubicado en app/.../network/CacheManager.kt. Es consumido por los Repositories mediante CacheManager.getInstance(context):

Repository MΓ©todo Dato cacheado
AlbumRepositoryImpl getAlbums() Lista de Γ‘lbumes
AlbumRepositoryImpl getAlbumById(id) Detalle de Γ‘lbum por ID
MusicianRepositoryImpl getMusicians() Lista de mΓΊsicos
MusicianRepositoryImpl getMusicianById(id) Detalle de mΓΊsico por ID
MusicianRepositoryImpl getPerformerPrizesByMusicianId(id) Premios por mΓΊsico
CollectorRepositoryImpl getCollectors() Lista de coleccionistas

Con quΓ© fin

Cada Repository aplica el patrΓ³n Cache-Aside: consulta primero el CacheManager y solo va a red si no hay datos. Ejemplo real del cΓ³digo (AlbumRepositoryImpl):

override suspend fun getAlbums(): List<Album> {
    val potentialResp = CacheManager.getInstance(context).getAlbums()
    return if (potentialResp.isEmpty()) {
        Log.d("Cache decision", "get albums from network")
        val albums = remoteDataSource.fetchAlbums()
        CacheManager.getInstance(context).addAlbums(albums)
        albums
    } else {
        Log.d("Cache decision", "return ${potentialResp.size} albums from cache")
        potentialResp
    }
}

El fin es evitar llamadas HTTP repetidas cuando los datos ya fueron obtenidos durante la sesiΓ³n.

Mejoras que favorece

  • Velocidad de navegaciΓ³n: Accesos repetidos a la misma pantalla se resuelven en memoria (~ms) sin esperar respuesta de red (~1-2s).
  • ReducciΓ³n de transferencia de datos: Solo se descarga la primera vez; navegaciones subsecuentes no generan trΓ‘fico.
  • Menor uso de radio (baterΓ­a): Menos conexiones HTTP activas = menos tiempo con radio LTE/WiFi encendida.
  • Fluidez en UI: Los ViewModels reciben datos inmediatos, LiveData se actualiza sin delays perceptibles.

2. Estrategia: CachΓ© de ImΓ‘genes (Glide + OkHttp3)

DΓ³nde se usa

Configurado en app/.../MyAppGlideModule.kt con integraciΓ³n OkHttp3. Se consume en 4 componentes de UI:

Componente Imagen cacheada
AlbumAdapter.kt Portada de Γ‘lbum (album.cover) en lista
AlbumDetailFragment.kt Portada de Γ‘lbum en vista de detalle
MusicianAdapter.kt Foto de mΓΊsico (musician.image) en lista
MusicianDetailFragment.kt Foto de mΓΊsico en vista de detalle

ConfiguraciΓ³n real:

@GlideModule
class MyAppGlideModule : AppGlideModule() {
    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        val client = OkHttpClient.Builder()
            .addInterceptor { chain ->
                val request = chain.request().newBuilder()
                    .header("User-Agent", "Mozilla/5.0")
                    .build()
                chain.proceed(request)
            }
            .build()
        registry.replace(GlideUrl::class.java, InputStream::class.java,
            OkHttpUrlLoader.Factory(client))
    }
}

Con quΓ© fin

Glide gestiona automΓ‘ticamente un cachΓ© de dos niveles (memoria LRU + disco) para imΓ‘genes. El fin es:

  • Evitar descargar la misma imagen mΓ‘s de una vez por sesiΓ³n (memory cache).
  • Conservar imΓ‘genes entre sesiones (disk cache).
  • Desacoplar la carga de imΓ‘genes del hilo principal de UI.

Mejoras que favorece

  • Scroll fluido: Las imΓ‘genes ya cargadas se muestran instantΓ‘neamente al hacer scroll, sin flicker ni reloads.
  • Ahorro de datos: ImΓ‘genes (tΓ­picamente 20-100 KB cada una) se descargan una sola vez y se reutilizan.
  • GestiΓ³n de memoria automΓ‘tica: Glide usa LRU β€” libera imΓ‘genes menos usadas cuando la memoria lo requiere, sin intervenciΓ³n del desarrollador.
  • Carga asincrΓ³nica: Las imΓ‘genes se cargan en background; la UI no se bloquea mientras se descargan o decodifican.

3. Diagrama de Arquitectura

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ UI Layer                                                 β”‚
β”‚  AlbumListFragment Β· AlbumDetailFragment                 β”‚
β”‚  MusicianListFragment Β· MusicianDetailFragment           β”‚
β”‚  CollectorListFragment                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚ observa LiveData
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ViewModels                                               β”‚
β”‚  AlbumViewModel Β· AlbumDetailViewModel                   β”‚
β”‚  MusicianViewModel Β· MusicianDetailViewModel             β”‚
β”‚  CollectorViewModel                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚ suspend fun
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Repository Layer (lΓ³gica Cache-Aside)                    β”‚
β”‚  AlbumRepositoryImpl                                     β”‚
β”‚  MusicianRepositoryImpl                                  β”‚
β”‚  CollectorRepositoryImpl                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                                β”‚
     Cache Hit                        Cache Miss
           β”‚                                β”‚
           β–Ό                                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ CacheManager        β”‚         β”‚ RemoteDataSource        β”‚
β”‚ (Singleton)         β”‚         β”‚  β†’ Retrofit + OkHttp3   β”‚
β”‚                     β”‚         β”‚  β†’ Backend API          β”‚
β”‚ albums: List        β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ musicians: List     β”‚                      β”‚
β”‚ collectors: List    β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ albumDetails: Map   β”‚     guarda en cachΓ©
β”‚ musicianDetails: Mapβ”‚
β”‚ performerPrizes: Mapβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Glide (paralelo, en UI Layer)                            β”‚
β”‚                                                          β”‚
β”‚  ImageView.load(url)                                     β”‚
β”‚       ↓                                                  β”‚
β”‚  Memory LRU β†’ Disk Cache β†’ Network (OkHttp3)            β”‚
β”‚       ↓           ↓              ↓                       β”‚
β”‚    instant     ~10-50ms      ~500-1500ms                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4. Resumen por Pantalla

Pantalla CachΓ© de datos CachΓ© de imΓ‘genes Beneficio observable
Lista de Γ‘lbumes getAlbums() β†’ lista en memoria Portadas via Glide Segunda visita instantΓ‘nea, scroll fluido
Detalle de Γ‘lbum getAlbumDetail(id) β†’ mapa por ID Portada grande via Glide Revisitar mismo Γ‘lbum sin espera
Lista de mΓΊsicos getMusicians() β†’ lista en memoria Fotos via Glide NavegaciΓ³n sin recarga
Detalle de mΓΊsico getMusicianDetail(id) + getPerformerPrizes(id) Foto via Glide Premios y datos cargados al instante
Lista de coleccionistas getCollectors() β†’ lista en memoria β€” Datos disponibles sin red

5. CorrelaciΓ³n con Perfilamiento

Referencia: Perfilamiento y resultados Sprint 2.xlsx

Las estrategias de cachΓ© implementadas se correlacionan directamente con mejoras en las mΓ©tricas de rendimiento:

  • Tiempo de respuesta de pantallas: El patrΓ³n Cache-Aside reduce la latencia de accesos repetidos de ~1-2 segundos (red) a milisegundos (memoria). Cualquier mediciΓ³n de tiempo de carga en segunda visita deberΓ­a reflejar esta mejora.
  • Consumo de memoria: El footprint de CacheManager es bajo (listas y mapas de objetos ligeros). Glide gestiona su propia memoria con LRU adaptativo. No se observa degradaciΓ³n por uso de cachΓ©.
  • Transferencia de red: Las llamadas HTTP se reducen significativamente β€” solo se ejecutan en cache-miss (primera carga). Esto es verificable en las mΓ©tricas de red del perfilamiento.

El caching actΓΊa como estrategia de optimizaciΓ³n de recursos al intercambiar un mΓ­nimo de memoria (~KB) por ahorro sustancial en tiempo, datos y energΓ­a.


6. ConclusiΓ³n

La aplicaciΓ³n utiliza dos estrategias de caching complementarias:

Estrategia Recurso optimizado Mecanismo
CacheManager Red, latencia, baterΓ­a Singleton en memoria con Cache-Aside
Glide Red, memoria de imΓ‘genes, UX LRU automΓ‘tico (memoria + disco)

Ambas operan de forma transparente para la UI: los ViewModels y Fragments consumen datos sin conocer si provienen de cachΓ© o de red. La decisiΓ³n se centraliza en la capa Repository (datos) y en Glide (imΓ‘genes), lo que mantiene la arquitectura limpia y la lΓ³gica de caching aislada.

El resultado es una aplicaciΓ³n que responde rΓ‘pido en navegaciones frecuentes, consume menos datos mΓ³viles y optimiza el uso de baterΓ­a β€” todo con un costo de memoria negligible.


TecnologΓ­as: Glide 5.0.7 Β· OkHttp3 5.3.2 Β· Retrofit2 3.0.0 Β· Kotlin Coroutines

⚠️ **GitHub.com Fallback** ⚠️