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

Estrategias de Caching - OptimizaciΓ³n de Memoria

1. Continuidad desde Sprint 1

1.1 Estrategias de caching que continΓΊan activas

Las estrategias implementadas en el Sprint 2 se mantienen como base del sistema de cachΓ© de la aplicaciΓ³n:

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

El patrΓ³n Cache-Aside sigue operando en todos los repositorios existentes:

Repositorio 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

2. Nuevas estrategias de caching - Sprint 3

2.1 RefactorizaciΓ³n de CacheManager - eliminaciΓ³n de dependencia de Context

Caso optimizado: En el Sprint 2, CacheManager requerΓ­a un Context como parΓ‘metro en cada llamada a getInstance(context), acoplando el sistema de cachΓ© a la capa de Android y dificultando su uso en capas de datos puras.

OptimizaciΓ³n implementada: CacheManager fue refactorizado para ser independiente del contexto. Todas las llamadas pasaron de:

// Sprint 2
CacheManager.getInstance(context).getAlbums()
CacheManager.getInstance(context).addAlbums(albums)

a:

// Sprint 3
CacheManager.getInstance().getAlbums()
CacheManager.getInstance().addAlbums()

Beneficio: El CacheManager ahora puede ser consumido desde cualquier capa sin necesidad de propagar el Context, simplificando los repositorios y reduciendo el acoplamiento arquitectural.

2.2 Caching de detalle de coleccionista

La consulta del detalle de un coleccionista fue una funcionalidad aΓ±adida en este sprint. Siguiendo el patrΓ³n establecido por las micro-optimizaciones, el detalle de un coleccionista tambiΓ©n es guardado en cachΓ©:

override suspend fun getCollectorById(id: Int): Collector {
    val potentialResp = CacheManager.getInstance().getCollectorDetail(id)
    return if (potentialResp == null) {
        Log.d("Cache decision", "get collector $id from network")
        val collector = remoteDataSource.fetchCollectorById(id)
        CacheManager.getInstance().addCollectorDetail(id, collector)
        collector
    } else {
        Log.d("Cache decision", "return collector $id from cache")
        potentialResp
    }
}

Beneficio observable: La segunda apertura del detalle de un coleccionista resuelve su informaciΓ³n en memoria (~ms) sin llamadas a red, reduciendo la latencia percibida y el consumo de datos.

2.3 Caching de datos de formulario (GΓ©neros y Sellos DiscogrΓ‘ficos)

Para el formulario de creaciΓ³n de un Γ‘lbum, como existe la posibilidad de obtener los gΓ©neros y sellos discogrΓ‘ficos de manera remota, se incorporΓ³ cachΓ© para almacenar sus datos. Ejemplo:

override suspend fun getGenres(): List<String> {
    val potentialResp = CacheManager.getInstance().getGenres()
    return if (potentialResp.isEmpty()) {
        Log.d("Cache decision", "get genres from network")
        val genres = remoteDataSource.fetchGenres()
        CacheManager.getInstance().addGenres(genres)
        genres
    } else {
        Log.d("Cache decision", "return ${potentialResp.size} genres from cache")
        potentialResp
    }
}

Beneficio observable: La segunda apertura del formulario de creaciΓ³n resuleve gΓ©neros y sellos en memoria (~ms) sin llamadas a red, reduciendo la latencia percibida y el consumo de datos.

2.4 InvalidaciΓ³n de cachΓ© en operaciones de escritura

Caso optimizado: En el Sprint 2, el cachΓ© no se invalidaba al crear o modificar datos, lo que podΓ­a ocasionar que la app mostrar informaciΓ³n obsoleta tras una operaciΓ³n de escritura exitosa.

OptimizaciΓ³n implementada: Se incorporΓ³ invalidaciΓ³n selectiva del cachΓ© en las operaciones de mutaciΓ³n de AlbumRepositoryImpl:

OperaciΓ³n CachΓ© invalidado Motivo
createAlbum() clearAlbums() La lista de Γ‘lbumes cambiΓ³, debe refrescarse
addTrack() clearAlbumDetail() El detalle del Γ‘lbum cambiΓ³, debe refrescarse
override suspend fun createAlbum(album: AlbumRequest): Album {
    val result = remoteDataSource.createAlbum(album)
    // Clear albums cache to force refresh
    CacheManager.getInstance().clearAlbums()
    return result
}

override suspend fun addTrack(albumId: Int, track: TrackRequest): Track {
    val result = remoteDataSource.addTrack(albumId, track)
    // Clear album detail cache to force refresh
    CacheManager.getInstance().clearAlbumDetail(albumId)
    return result
}

Beneficio: Garantiza consistencia entre los datos mostrados en UI y el estado real del backend despuΓ©s de cualquier operaciΓ³n de escritura, evitando que el usuario vea datos obsoletos.


3. Diagrama de arquitectura de caching actualizado

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ UI Layer                                                 β”‚
β”‚  AlbumListFragment Β· AlbumDetailFragment                 β”‚
β”‚  AlbumCreateFragment Β· TrackAssociateFragment            β”‚
β”‚  MusicianListFragment Β· MusicianDetailFragment           β”‚
β”‚  CollectorListFragment                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚ observa LiveData
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ViewModels                                               β”‚
β”‚  AlbumViewModel Β· AlbumDetailViewModel                   β”‚
β”‚  AlbumCreateViewModel Β· TrackAssociateViewModel          β”‚
β”‚  MusicianViewModel Β· MusicianDetailViewModel             β”‚
β”‚  CollectorViewModel                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚ suspend fun
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Repository Layer (lΓ³gica Cache-Aside + InvalidaciΓ³n)     β”‚
β”‚  AlbumRepositoryImpl                                     β”‚
β”‚  MusicianRepositoryImpl                                  β”‚
β”‚  CollectorRepositoryImpl                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                          β”‚
     Cache Hit                  Cache Miss / Escritura
           β”‚                          β”‚
           β–Ό                          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ CacheManager        β”‚   β”‚ RemoteDataSource        β”‚
β”‚ (Singleton)         β”‚   β”‚  β†’ Retrofit + OkHttp3   β”‚
β”‚ sin Context ← NUEVO β”‚   β”‚  β†’ Backend API          β”‚
β”‚                     β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ albums: List        β”‚                β”‚
β”‚ musicians: List     β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ collectors: List    β”‚   guarda en cachΓ©
β”‚ albumDetails: Map   β”‚
β”‚ musicianDetails: Mapβ”‚
β”‚ performerPrizes: Mapβ”‚
β”‚ genres: List     ← NUEVO Sprint 3
β”‚ recordLabels: List ← NUEVO Sprint 3
β”‚                     β”‚
β”‚ clearAlbums()    ← NUEVO: invalidaciΓ³n
β”‚ clearAlbumDetail()← NUEVO: invalidaciΓ³n
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

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

4. Resumen por pantalla actualizado

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
Crear Γ‘lbum getGenres() + getRecordLabels() β†’ listas en memoria - Formulario abre sin llamadas a red en segunda apertura
clearAlbums() tras Γ©xito - Listado de Γ‘lbumes siempre refleja el Γ‘lbum aΓ±adido
Asociar canciΓ³n clearAlbumDetail() tras Γ©xito - Detalle del Γ‘lbum siempre refleja tracks actualizados
Detalle de mΓΊsico getMusicianById(id) + getPerformerPrizes(id) Foto via Glide Premios y datos cargados al instante
Lista de coleccionistas getCollectors() β†’ lista en memoria - Datos disponibles sin red
Detalle de coleccionista getCollectorById(id) β†’ mapa por ID Fotos de artistas y portadas de Γ‘lbumes via Glide Revisitar mismo coleccionista sin espera

5. CorrelaciΓ³n con Perfilamiento

Referencia: Perfilamiento y resultados Sprint 3.xlsx

Las estrategias de caching implementadas se correlacionan directamente con cambios observables en las mΓ©tricas de memoria y concurrencia obtenidas durante el perfilamiento:

  • GestiΓ³n de hilos y concurrencia: El uso de CacheManager, Glide y caching de servicios Retrofit reduce operaciones repetitivas de red y recreaciones innecesarias de objetos, contribuyendo a estabilizar parcialmente la cantidad de hilos en varias HUs, especialmente en Android 13. En Android 15 y 16 el comportamiento es mΓ‘s variable debido a mecanismos internos de scheduling y administraciΓ³n de tareas.

  • Consumo de memoria: La reutilizaciΓ³n de datos cacheados y de objetos pesados como SimpleDateFormat ayuda a disminuir recreaciones innecesarias en memoria. Sin embargo, optimizaciones como RecyclerView y DiffUtil requieren estructuras auxiliares para reciclar vistas y calcular diferencias, por lo que algunas HUs presentan incrementos moderados de RAM, particularmente en Android 15 y 16.

  • Renderizado y trabajo de UI: La reutilizaciΓ³n de datos en cachΓ© reduce recargas completas de pantallas y solicitudes HTTP redundantes. Adicionalmente, optimizaciones como la reducciΓ³n de overdraw, el caching de instancias Retrofit y la migraciΓ³n hacia RecyclerView disminuyen trabajo redundante de UI y recreaciΓ³n innecesaria de layouts.

Las estrategias de caching y micro-optimizaciones actΓΊan conjuntamente como mecanismos de reducciΓ³n de trabajo redundante, intercambiando un pequeΓ±o costo adicional de memoria auxiliar por ahorro en tiempo de respuesta, reutilizaciΓ³n de recursos y menor cantidad de operaciones repetitivas de UI y red.


6. ConclusiΓ³n

En el Sprint 3 se extiende la estrategia de caching del Sprint 2 en tres dimensiones:

  1. RefactorizaciΓ³n estructural: CacheManager eliminΓ³ su dependencia de Context, mejorando la arquitectura y testeabilidad.
  2. Cobertura de nuevos datos: El detalle de un coleccionista, los gΓ©neros y los sellos discogrΓ‘ficos se cachean, completando la cobertura para todos los flujos de lectura incluyendo el de creaciΓ³n.
  3. Consistencia tras escritura: La invalidaciΓ³n selectiva de cachΓ© garantiza que los datos mostrados en UI reflejen siempre el estado real del backend.
Estrategia Recurso optimizado Mecanismo
CacheManager Red, latencia, baterΓ­a Singleton en memoria con Cache-Aside + InvalidaciΓ³n
Glide Red, memoria de imΓ‘genes, UX LRU automΓ‘tico (memoria + disco)
Micro-optimizaciones CPU, GPU, memoria Caching de instancias, reducciΓ³n de jerarquΓ­a, limpieza de recursos

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

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