Casi todas las aplicaciones que usamos o desarrollamos en Android necesitan almacenar datos de forma persistente: configuraciones del usuario, sesiones, archivos, información estructurada o estados de la interfaz. Aunque el concepto de persistencia no ha cambiado con los años, las herramientas, APIs y buenas prácticas sí lo han hecho radicalmente.
En términos simples, persistir datos significa que la información sobrevive al cierre de la aplicación, a cambios de configuración e incluso al reinicio del dispositivo. En Android moderno, elegir mal el mecanismo de almacenamiento puede provocar pérdida de datos, problemas de rendimiento o incluso el rechazo de la app en Google Play.
En este artículo se analizan todas las técnicas actuales de almacenamiento de datos persistente en Android Studio, partiendo de los conceptos clásicos y llevándolos al estado actual de Android (Jetpack, Kotlin y Compose).
En versiones modernas de Android, elegir incorrectamente el mecanismo de almacenamiento puede provocar:
- Problemas de rendimiento (bloqueos del hilo principal)
- Riesgos de seguridad y privacidad
- Pérdida de datos ante cambios de configuración
- Rechazo de la aplicación en Google Play
En términos simples, persistir datos significa que la información sobrevive al cierre de la app, a cambios de configuración y, en muchos casos, al reinicio del dispositivo. Android actual ya no apuesta por una única solución, sino por una estrategia combinada de persistencia.
En esta guía ampliada y actualizada veremos todas las opciones actuales de almacenamiento de datos persistente en Android Studio, cuándo usar cada una, ejemplos prácticos y qué enfoques han quedado obsoletos.
¿Qué es la persistencia en Android?
En informática, la persistencia consiste en lograr que los datos manipulados por una aplicación sobrevivan en el tiempo, independientemente del proceso que los creó. Coloquialmente hablando: que los datos no se borren cuando la app se cierra.
En Android moderno, la persistencia se divide en tres grandes niveles, cada uno con un propósito distinto:
- Estado de la IU (memoria): datos temporales necesarios para que la interfaz no se reinicie en rotaciones o cambios de configuración.
- Persistencia local (disco): datos que deben sobrevivir al cierre de la app.
- Persistencia estructurada: datos relacionales o consultables.
Una aplicación bien diseñada combina varios mecanismos, no depende de uno solo.
Android recomienda explícitamente no mezclar responsabilidades. Un error común en artículos antiguos es usar bases de datos o archivos para guardar estado de IU, lo cual hoy se considera una mala práctica.
1. Preferencias compartidas (SharedPreferences)
Las SharedPreferences permiten almacenar pares clave–valor simples como booleanos, números o texto. Históricamente fueron el mecanismo más utilizado para guardar configuraciones del usuario.
Características
- Almacenamiento en archivo XML privado
- No requiere permisos
- Ideal para pequeñas configuraciones
Durante muchos años, SharedPreferences fue la solución estándar para guardar pares clave–valor simples.
Qué permite almacenar:
- Boolean
- Int, Long, Float
- String
- Set
Ejemplo clásico:
Problemas detectados con el tiempo:
- Escrituras síncronas si se usa
commit() - Riesgo de corrupción
- Sin control de concurrencia
- Sin tipado fuerte
Limitaciones actuales
- Acceso síncrono potencial
- Riesgo de inconsistencias
- Sin seguridad de tipos
Por estos motivos, ya no se recomienda como solución principal en apps nuevas.
1.2 DataStore (reemplazo moderno y recomendado)
DataStore forma parte de Jetpack y es el reemplazo oficial de SharedPreferences.
Existen dos variantes:
DataStore Preferences
Ideal para configuraciones simples.
Lectura reactiva:
Proto DataStore
Usado cuando necesitas esquema fuerte y validación.
- Ideal para apps grandes
- Usa Protobuf
- Evita errores por claves incorrectas
Cuándo usar DataStore:
- Preferencias de usuario
- Flags de configuración
- Datos pequeños pero persistentes
Ventajas
- Asíncrono
- Seguro frente a corrupción
- Integración con corrutinas
- Observación reactiva con Flow
Cuándo usar DataStore
- Preferencias de usuario
- Flags de configuración
- Estados simples persistentes
2. Almacenamiento de archivos
2.1 Memoria interna (privada y segura)
La memoria interna sigue siendo una de las opciones más seguras y simples.
val file = File(filesDir, "config.txt")
file.writeText("Hola Mundo")Características:
- Solo accesible por la app
- Se elimina al desinstalar
- Ideal para datos sensibles
Características:
- No requiere permisos
- Solo accesible por la app
- Se elimina al desinstalar
Ejemplo en Kotlin:
Lectura:
Casos de uso típicos:
- Archivos de configuración
- Tokens cifrados
- Datos privados de la app
2.2 Memoria externa y Scoped Storage
Desde Android 10 (API 29), Google introdujo Scoped Storage.
Qué cambió:
- Acceso libre a la SD eliminado
- Permisos WRITE/READ_EXTERNAL_STORAGE obsoletos
- Acceso limitado por sandbox
Ejemplo recomendado:
Para acceso a archivos creados por otras apps se debe usar:
- Storage Access Framework (SAF)
- Intent ACTION_OPEN_DOCUMENT
Importante: el uso incorrecto de storage es una de las causas más comunes de rechazo en Google Play.
3. Bases de datos: SQLite vs Room
3.1 SQLite directo (bajo nivel)
SQLite sigue siendo el motor base, pero manejarlo manualmente implica:
- Código repetitivo
- Errores en runtime
- Difícil mantenimiento
Hoy se considera una opción de bajo nivel.
Características
- Base de datos en un solo archivo
- No cliente-servidor
- Rápida y ligera
3.2 Room (estándar actual)
Room es la capa de abstracción oficial de Android Jetpack sobre SQLite.
Componentes:
- Entity
- DAO
- Database
Ejemplo completo:
Ventajas reales:
- Verificación en compilación
- Integración con Flow y LiveData
- Fácil testing
4. Caché: datos temporales
La caché permite almacenar datos reconstruibles.
Ejemplo:
Buenas prácticas:
- Nunca datos críticos
- Limitar tamaño
- El sistema puede eliminarla
Buenas prácticas:
- Nunca guardar datos críticos
- Limitar tamaño
- Limpiar periódicamente
5. Estado de la IU: ViewModel y SavedState
5.1 ViewModel
Diseñado para:
- Sobrevivir a rotaciones
- Evitar recargas innecesarias
5.2 SavedStateHandle
Permite restaurar estado tras cierre del proceso.
Regla clave:
Estado de IU no debe guardarse en base de datos.
ViewModel
- Mantiene datos en memoria
- Sobrevive a rotaciones
- No sobrevive al kill del proceso
SavedState / rememberSaveable
- Backup ligero
- Solo datos pequeños
6. Persistencia avanzada: Ink API y serialización
En apps de dibujo:
- Serializar StrokeInputBatch
- Guardar Brush por separado
- Exportar a imagen
Este enfoque es esencial para:
- Apps de notas
- Whiteboards
- Colaboración en tiempo real
¿Qué mecanismo debo usar?
| Necesidad | Solución recomendada |
|---|---|
| Preferencias | DataStore |
| Estado de IU | ViewModel + SavedState |
| Datos relacionales | Room |
| Archivos privados | Memoria interna |
| Archivos compartidos | SAF / Scoped Storage |
| Datos temporales | Caché |
Conclusión
La persistencia en Android moderno no se basa en una única técnica, sino en una arquitectura bien pensada:
- ViewModel para IU
- DataStore para preferencias
- Room para datos complejos
- Scoped Storage para archivos
Actualizar estos conceptos es esencial para crear aplicaciones seguras, eficientes y alineadas con los estándares actuales de Android Studio.
7. Ejemplo end-to-end: App completa con ViewModel + DataStore + Room + UI
Escenario
Aplicación sencilla de tareas (To‑Do) que:
- Guarda preferencias (modo oscuro)
- Persiste tareas en base de datos
- Mantiene estado de IU ante rotaciones
Arquitectura
- UI: Jetpack Compose
- Estado: ViewModel + StateFlow
- Preferencias: DataStore
- Datos: Room
DataStore – Preferencias
Room – Base de datos
ViewModel – Lógica de estado
UI – Jetpack Compose
8. Versión 100% Jetpack Compose
rememberSaveable
Se usa para:
- Texto de inputs
- Scroll
- Selecciones temporales
StateFlow + Compose
Ventajas:
- Reactivo
- Lifecycle‑aware
- Fácil testing
Room + Flow + Compose
- Room expone Flow
- ViewModel lo transforma
- Compose lo observa
Esto evita:
- Callbacks
- Estados inconsistentes
9. Tabla comparativa avanzada
| Mecanismo | Rendimiento | Sobrevive kill proceso | Casos reales | Errores comunes |
| ViewModel | Muy alto | ❌ | Estado UI | Guardar datos persistentes |
| SavedState | Medio | ✔️ | Inputs, scro |