estrategia de caching sprint 2 - jcrinconv/MISW4203-2026-12-ing-sw-apps-moviles GitHub Wiki
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 |
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.
- 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.
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))
}
}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.
- 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.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| 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 |
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.
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