Prefiere el almacenamiento de datos con Jetpack DataStore

Publicado por Florina Muntenescu Defensora de desarrolladores de Android,
Rohit Sathyanarayana, ingeniero de software

Bienvenido Jetpack DataStore, ahora en versión alfa, una nueva y mejorada solución de almacenamiento de datos para reemplazar SharedPreferences. Basado en la co-rutina de Kotlin y Flow, DataStore proporciona dos implementaciones diferentes: Proto DataStore que le permite almacenar objetos con tipo (admitidos por el búfer de protocolo) y Preferencias de DataStore que almacena pares clave-valor . Los datos se almacenan de forma asincrónica, coherente y transaccional, superando la mayoría de las desventajas de SharedPreferences.

SharedPreferences vs DataStore

  SharedPreferences

* SharedPreferences tiene una API síncrona que puede parecer segura para llamar en el subproceso de IU, pero que en realidad realiza operaciones de E / S de disco. Además, apply () bloquea el subproceso de la interfaz de usuario en fsync () . Las llamadas pendientes fsync () se activan cada vez que se inicia o se detiene un servicio y cada vez que se inicia o detiene una actividad en cualquier lugar de la aplicación. El subproceso de la interfaz de usuario se bloquea durante las llamadas pendientes fsync () programadas por apply () convirtiéndose a menudo en una fuente de ANR.

** SharedPreferences arroja errores de análisis como excepciones en tiempo de ejecución.

En ambas implementaciones, el almacén de datos guarda las preferencias en un archivo y realiza todas las operaciones de datos en Dispatchers.IO a menos que se especifique lo contrario.

Aunque tanto Preferences DataStore como Proto DataStore permiten guardar datos, lo hacen de diferentes formas:

  • Preference DataStore como SharedPreferences, no tiene forma de definir un esquema o garantizar que se acceda a las claves con el tipo correcto.
  • Proto DataStore le permite definir un esquema usando los búferes de protocolo. El uso de Protobufs permite conservar datos fuertemente tipados . Son más rápidos, más pequeños, más simples y menos ambiguos que XML y otros formatos de datos similares. Aunque Proto DataStore requiere aprender un nuevo mecanismo de serialización, creemos que la ventaja del esquema fuertemente tipado que ofrece Proto DataStore vale la pena.

Room vs DataStore

Si necesita actualizaciones parciales, integridad referencial o soporte para conjuntos de datos grandes / complejos, debería considerar usar Room en lugar de DataStore. DataStore es ideal para conjuntos de datos pequeños y simples y no admite actualizaciones parciales ni integridad referencial.

Comience agregando la dependencia de DataStore. Si está utilizando Proto DataStore, asegúrese de agregar también la dependencia de proto:

  // Preferencias del almacén de datos
implementación "androidx.datastore: datastore-Preferences: 1.0.0-alpha01"


// Proto DataStore
implementación "androidx.datastore: datastore-core: 1.0.0-alpha01" 

Cuando se trabaja con Proto DataStore, se define el esquema en un archivo proto en el directorio app / src / main / proto / . Consulte la Guía de lenguaje de Protobuf para obtener más información sobre cómo definir un esquema de protobuf.

  syntax = "proto3";

opción java_package = "";
opción java_multiple_files = true;

Configuración de mensajes {
  int my_counter = 1;
} 

Cree el DataStore

Cree el DataStore con las funciones de extensión Context.createDataStore () .

  // con preferencias de DataStore
val dataStore: DataStore  = context.createDataStore (
    nombre = "configuración"
) 

Si está usando Proto DataStore, también necesitará implementar la interfaz Serializador para indicarle a DataStore cómo leer y escribir su tipo de datos.

  Configuración de objetoSerializer: Serializer  {
    anular la diversión readFrom (input: InputStream): Settings {
        tratar {
            return Settings.parseFrom (entrada)
        } captura (excepción: InvalidProtocolBufferException) {
            lanzar CorruptionException ("No se puede leer proto.", excepción)
        }
    }

    sobrescribir divertido writeTo (t: Configuración, salida: OutputStream) = t.writeTo (salida)
}


// con Proto DataStore
val settingsDataStore: DataStore  = context.createDataStore (
    fileName = "settings.pb",
    serializer = SettingsSerializer
) 

Leer datos de DataStore

DataStore expone los datos almacenados en un Stream en un objeto Preferences o como el objeto definido en el esquema proto. DataStore garantiza que los datos se recuperen en Dispatchers.IO para que el subproceso de la interfaz de usuario no se bloquee.

Con las preferencias de DataStore:

  val MY_COUNTER = PreferencesKey  ("my_counter")
val myCounterFlow: Flow  = dataStore.data
     .map {currentPreferences ->
        // A diferencia de Proto DataStore, aquí no hay seguridad de tipos.
        currentPreferences [MY_COUNTER]?: 0
   } 

Con Proto DataStore:

  val myCounterFlow: Flow  = settingsDataStore.data
    .map {configuración ->
        // ¡La propiedad myCounter es generada por su esquema proto!
        settings.myCounter
    } 

Escribir datos en DataStore

Para escribir datos, DataStore ofrece una función de suspensión DataStore.updateData () que le brinda el estado actual de los datos almacenados como parámetro o como objeto Preferences o una instancia del objeto definido en el protoesquema. La función updateData () actualiza los datos a nivel transaccional en una operación atómica de lectura-escritura-modificación. La corrutina se completa una vez que los datos son persistentes en el disco.

Preferencias de DataStore también proporciona una función DataStore.edit () para simplificar la actualización de datos. En lugar de recibir un elemento Preferencias recibe un elemento MutablePreferences que edita. Al igual que con updateData () los cambios se aplican al disco después de que se completa el bloque de transformación y la corrutina se completa una vez que los datos persisten en el disco.

Con las preferencias de DataStore:

  suspender diversión incrementCounter () {
    dataStore.edit {configuración ->
        // ¡Podemos aumentar nuestro contador de forma segura sin perder datos debido a las carreras!
        val currentCounterValue = settings [MY_COUNTER]?: 0
        configuración [MY_COUNTER] = currentCounterValue + 1
    }
} 

Con Proto DataStore:

  suspender diversión incrementCounter () {
    settingsDataStore.updateData {currentSettings ->
        // ¡Podemos aumentar nuestro contador de forma segura sin perder datos debido a las carreras!
        currentSettings.toBuilder ()
            .setMyCounter (currentSettings.myCounter + 1)
            .construir()
    }
} 

Migrar de SharedPreferences a DataStore

Para migrar de SharedPreferences a DataStore, debe pasar un objeto SharedPreferencesMigration al constructor de DataStore. DataStore puede migrar automáticamente de SharedPreferences a DataStore por usted. Las migraciones se realizan antes de que se pueda acceder a los datos del almacén de datos. Esto significa que la migración debe ser exitosa antes de que DataStore.data devuelva los valores y antes de que DataStore.updateData () pueda actualizar los datos.

Si está migrando a Preferences DataStore, puede usar la implementación predeterminada SharedPreferencesMigration y simplemente pasar el nombre usado para construir sus SharedPreferences.

Con las preferencias de DataStore:

  val dataStore: DataStore  = context.createDataStore (
    name = "configuración",
    migraciones = listOf (SharedPreferencesMigration (contexto, "settings_preferences"))
) 

Al migrar a Proto DataStore, deberá implementar una función de mapeo que defina cómo migrar desde los pares clave-valor utilizados por SharedPreferences al esquema de DataStore definido.

Con Proto DataStore:

  val settingsDataStore: DataStore  = context.createDataStore (
    produceFile = {Archivo (context.filesDir, "settings.preferences_pb")},
    serializer = SettingsSerializer,
    migraciones = listOf (
        SharedPreferencesMigration (
            contexto,
            "settings_preferences"
        ) {sharedPrefs: SharedPreferencesView, currentData: UserPreferences ->
            // Asigne aquí sus preferencias compartidas a su tipo
          }
    )
) 

SharedPreferences tiene varios inconvenientes: una API síncrona que puede parecer segura para llamar en el subproceso de la IU, ningún mecanismo para informar errores, falta de API transaccionales y más. DataStore es un reemplazo de SharedPreferences que soluciona la mayoría de estas deficiencias. DataStore incluye una API totalmente asincrónica que utiliza las corrutinas Kotlin y Flow, gestiona la migración de datos, garantiza la coherencia de los datos y gestiona la corrupción de datos.

Dado que DataStore todavía está en versión alfa, ¡necesitamos su ayuda para mejorarlo! Para comenzar, obtenga más información sobre DataStore en nuestra documentación y pruébelo usando nuestros laboratorios de código: el laboratorio de códigos DataStore y las preferencias del laboratorio de códigos Proto DataStore. Entonces, háganos saber cómo podemos mejorar la biblioteca creando problemas en Issue Tracker.



Compruebe también

en vivo desde Droidcon, incluida la mayor actualización de Gemini en Android Studio y más lanzamientos del SDK de Android.

Acabamos de lanzar nuestro episodio de otoño de #TheAndroidShow en YouTube etcétera desarrollador.android.comy esta vez …

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *