Síntesis transversal de las guías 02 a 12
Las distinciones que cruzan varias guías: estado y evento, qué sobrevive a cada cambio, errores y reintentos, idempotencia, qué afirma cada tipo de prueba y seguridad por capas. Con preguntas integradoras al final.
Esta síntesis conecta conocimientos ya estudiados; no amplía el temario. Sirve para interpretar preguntas integradoras y distingue principios de decisiones particulares de las prácticas.
1. Estados, eventos, intenciones y fuentes de datos
| Término | Qué representa | Ejemplo | Advertencia |
|---|---|---|---|
| Intención | Acción pedida por el usuario | onPublicar() |
No es todavía el resultado |
| Estado actual | Situación que necesita dibujarse | StateFlow<UiState> |
Conserva el último valor, no historial |
| Evento efímero | Algo que ocurrió | SharedFlow<MensajeNuevo> |
Puede no llegar sin colector, según configuración |
| Fuente observable | Valores que proceden de datos | Flow<List<Tarea>> de Room |
Los datos persisten en Room, no por ser Flow |
| Adaptador de callbacks | Conecta API de eventos con Flow | callbackFlow de SSE |
Requiere liberar recursos (awaitClose) |
Pregunta de transferencia: si el usuario abandona la pantalla y vuelve, ¿necesita reconstruir un estado, recibir un evento histórico o consultar la fuente de datos? La respuesta decide la herramienta y las garantías.
2. Qué sobrevive a cada cambio
| Mecanismo | Recomposición | Cambio de configuración | Muerte recuperable del proceso | Cierre explícito o borrado de datos |
|---|---|---|---|---|
remember |
Sí si permanece en composición | Generalmente no | No | No |
ViewModel |
Sí | Generalmente sí, para el mismo owner | No, se crea otro | No |
rememberSaveable |
Sí | Usualmente sí con estado guardable | Puede restaurarse si Android conserva/restaura estado | No garantizado |
SavedStateHandle |
Sí, vía ViewModel | Sí | Puede restaurar valores pequeños guardados | No garantizado |
| Room / DataStore | Sí | Sí | Sí, al estar guardados en disco | Depende de limpieza, desinstalación o política |
@Singleton de Hilt |
Sí durante su componente | Usualmente sí en el proceso | No conserva la instancia tras terminar proceso | No |
La restauración del estado guardado está condicionada al mecanismo de Android. No se debe escribir un Bitmap en él ni usarlo como sustituto de persistencia de negocio.
3. Errores y decisiones de recuperación
| Situación | Posible causa | Respuesta razonable | Límite |
|---|---|---|---|
IOException |
Red, transporte, conexión | Mostrar estado y/o reintentar con política | Escrituras podrían haberse completado |
401 en login |
Credenciales inválidas | Corregir credenciales | No intentar refresh indiscriminadamente |
401 con access expirado |
Token vencido según contrato | Refresh controlado y posible repetición | Rotación y coordinación concurrente |
401 en refresh |
Sesión inválida/revocada | Invalidar sesión si contrato lo confirma | Diferenciar fallo temporal de red |
403 |
Falta de permiso | Informar sin reintento ciego | Reintentar requiere cambiar permisos |
409 |
Conflicto | Resolver conflicto/duplicación | Depende de recurso y contrato |
413 / 422 |
Tamaño o validación | Corregir contenido | Repetir sin cambiarlo no sirve |
429 / 503 |
Límite o indisponibilidad | Espera y backoff, si procede | Respetar señales del servidor |
CancellationException |
Cancelación solicitada | Propagar y liberar recursos | No confundir con error recuperable |
4. Repetición de mensajes y operaciones
| Mecanismo | Protege frente a | No garantiza |
|---|---|---|
| Botón deshabilitado | Segundo toque mientras sigue enviando | Una sola escritura remota en todos los reintentos |
| Filtrar mensajes por ID en la UI | Duplicación visible | Ausencia de inserciones duplicadas en servidor |
notify(id, notificación) |
Dos notificaciones visibles con igual ID | Procesamiento exactamente una vez |
Last-Event-ID y cursor |
Solicitar reanudación desde un punto | Recuperación si servidor no retiene historial |
| Clave de idempotencia confirmada por API | Reintentos de una escritura equivalentes | Ausencia total de errores de negocio |
| Confirmaciones e IDs persistentes | Seguimiento de entrega y reenvíos | Semántica exactly-once automática |
5. Qué afirma cada tipo de prueba
| Tipo | Evidencia | Fuera de alcance |
|---|---|---|
| Unitaria local | Regla y sus casos ejercitados | Plataforma Android completa |
| Componente con fake | Comportamiento con dependencias controladas | Disponibilidad real del servicio |
| Instrumentada | Código Android y componentes incluidos | Todo dispositivo y condición |
| Compose UI Test | Propiedad semántica/interacción comprobada | Experiencia integral de accesibilidad |
| Integración | Contratos entre piezas reales incluidas | Servicios excluidos |
| End-to-end | Flujo concreto extremo a extremo | Otros recorridos y producción |
| Mutación | Sensibilidad ante mutaciones concretas | Ausencia general de defectos |
6. Seguridad a través de capas
Un recurso privado puede recorrer sesión → token → petición HTTP → caché → interfaz. La autorización se decide en el servidor. Un cliente que cifra tokens todavía debe considerar cachés locales, logs, cambio de cuenta y cierre de sesión. Un QR con una URL bien formada solo demuestra formato; no confiabilidad del servidor. Las rutas exactas de la práctica son ilustrativas.
7. Principio frente a elección
- Principio: liberar recursos al terminar su utilidad. Elección:
LifecycleStartEffectpara escuchar SSE cuando la pantalla está visible. - Principio: desacoplar construcción de uso. Elección: contenedor manual o Hilt.
- Principio: colores accesibles y adaptables. Elección: paleta fija de marca o Material You.
- Principio: persistir lo que debe sobrevivir al proceso. Elección: Room para entidades, DataStore para preferencias/sesión con protección adecuada.
- Principio: controlar fallos recuperables. Elección: reintentos según transporte, API y garantías de idempotencia.
8. Autoevaluación integradora
- Una pantalla observa Room y recibe eventos SSE. ¿Qué se reconstruye tras reabrir y qué podría perderse?
- El usuario toca «Publicar» una vez y la red se corta después de enviar la petición. ¿Por qué no basta deshabilitar el botón?
- Una prueba con fake verifica que se llamó a
crearRegistro(). ¿Qué queda sin comprobar? - Tras cambiar de cuenta, Coil sigue mostrando una imagen protegida. ¿Qué capas deben revisarse?
- Un WebSocket antiguo llama
onCloseddespués de que uno nuevo se conectó. ¿Qué comprobación evita el cambio incorrecto de estado y qué no garantiza?
Orientación para las respuestas
- Los registros de Room persisten; el stream requiere reanudación, historial o consulta según protocolo.
- El servidor pudo haber confirmado la escritura; hace falta una política de idempotencia/consulta y contrato del servidor.
- La API real, transporte, serialización e integración con servidor no están verificados por ese fake.
- Identidad/token, políticas de caché, separación por usuario, logout y autorización del servidor.
- Comparar identidad del socket actual en cada callback; no vuelve atómicas todas las operaciones concurrentes.