Publicado por Florina Muntenescu, Defensor del desarrollador 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 destinada a reemplazar las preferencias compartidas. Basado en la co-rutina de Kotlin y Flow, DataStore proporciona dos implementaciones diferentes: Proto DataStore, que te permite archivar objetos escritos (soportado por búfer de protocolo) e 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 tiene una API síncrona que puede parecer segura para llamar en el hilo de la interfaz de usuario, pero que en realidad realiza operaciones de E / S de disco. Además, apply()
bloquea el hilo de la interfaz de usuario fsync()
. esperando fsync()
las llamadas se activan cada vez que se inicia o detiene un servicio y cada vez que se inicia o se detiene una actividad en cualquier lugar de la aplicación. El hilo de la interfaz de usuario está atascado pendiente fsync()
llamadas programadas desde apply()
, convirtiéndose a menudo en una fuente de ANR.
** SharedPreferences genera errores de análisis como excepciones en tiempo de ejecución.
En ambas implementaciones, el DataStore guarda las preferencias en un archivo y realiza todas las operaciones sobre los 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:
- Preferencia de 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 utilizando búferes de protocolo. El uso de Protobufs permite la persistencia 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.
Habitación 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:
// Preferences DataStore implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01" // Proto DataStore implementation "androidx.datastore:datastore-core:1.0.0-alpha01"
Cuando trabaja con Proto DataStore, define su esquema en un proto en app/src/main/proto/
directorio. Consulte la Guía de lenguaje Protobuf para obtener más información sobre cómo definir un esquema protobuf.
syntax = "proto3"; option java_package = "<your package name here>"; option java_multiple_files = true; message Settings { int my_counter = 1; }
Crear el almacén de datos
Crea el archivo DataStore
con el Context.createDataStore()
funciones de extensión.
// with Preferences DataStore val dataStore: DataStore<Preferences> = context.createDataStore( name = "settings" )
Si está utilizando Proto DataStore, también deberá implementar el archivo Serializer
para indicarle al DataStore cómo leer y escribir el tipo de datos.
object SettingsSerializer : Serializer<Settings> { override fun readFrom(input: InputStream): Settings { try { return Settings.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output) } // with Proto DataStore val settingsDataStore: DataStore<Settings> = context.createDataStore( fileName = "settings.pb", serializer = SettingsSerializer )
Leer datos de DataStore
DataStore expone los datos almacenados en un archivo Flow
, ambos en un archivo Preferences
object o como el objeto definido en el protoesquema. DataStore garantiza que los datos se recuperen en Dispatchers.IO
por lo tanto, el hilo de la interfaz de usuario no está bloqueado.
Con las preferencias de DataStore:
val MY_COUNTER = preferencesKey<Int>("my_counter") val myCounterFlow: Flow<Int> = dataStore.data .map { currentPreferences -> // Unlike Proto DataStore, there's no type safety here. currentPreferences[MY_COUNTER] ?: 0 }
Con Proto DataStore:
val myCounterFlow: Flow<Int> = settingsDataStore.data .map { settings -> // The myCounter property is generated for you from your proto schema! settings.myCounter }
Escribir datos en DataStore
Para escribir los datos, DataStore ofrece una retención DataStore.updateData()
función que proporciona el estado actual de los datos almacenados como parámetro, tanto como archivo Preferences
objeto o una instancia del objeto definido en el protoesquema. los updateData()
La función 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 se conservan en el disco.
Preferencias de DataStore también proporciona una DataStore.edit()
función para facilitar la actualización de datos. En lugar de recibir un Preferences
artículo, recibe un MutablePreferences
objeto 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 se mantienen en el disco.
Con las preferencias de DataStore:
suspend fun incrementCounter() { dataStore.edit { settings -> // We can safely increment our counter without losing data due to races! val currentCounterValue = settings[MY_COUNTER] ?: 0 settings[MY_COUNTER] = currentCounterValue + 1 } }
Con Proto DataStore:
suspend fun incrementCounter() { settingsDataStore.updateData { currentSettings -> // We can safely increment our counter without losing data due to races! currentSettings.toBuilder() .setMyCounter(currentSettings.myCounter + 1) .build() } }
Migración de SharedPreferences a DataStore
Para migrar de SharedPreferences a DataStore, debe pasar un archivo SharedPreferencesMigration
objeto al constructor DataStore. DataStore puede migrar automáticamente de SharedPreferences a DataStore por usted. Las migraciones se realizan antes de que pueda ocurrir cualquier acceso a los datos en el DataStore. Esto significa que la migración debe haber tenido éxito primero. DataStore.data
devuelve cualquier valor y antes DataStore.updateData()
puede actualizar los datos.
Si está migrando a Preferences DataStore, puede utilizar el SharedPreferencesMigration
implementación y simplemente pase el nombre usado para construir sus SharedPreferences.
Con las preferencias de DataStore:
val dataStore: DataStore<Preferences> = context.createDataStore( name = "settings", migrations = listOf(SharedPreferencesMigration(context, "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<Settings> = context.createDataStore( produceFile = { File(context.filesDir, "settings.preferences_pb") }, serializer = SettingsSerializer, migrations = listOf( SharedPreferencesMigration( context, "settings_preferences" ) { sharedPrefs: SharedPreferencesView, currentData: UserPreferences -> // Map your sharedPrefs to your type here } ) )
SharedPreferences tiene varios inconvenientes: una API síncrona que puede parecer segura para llamar en el hilo de la interfaz de usuario, ningún mecanismo para informar errores, falta de API transaccional 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, administra la migración de datos, garantiza la coherencia de los datos y maneja la corrupción de datos.
Dado que DataStore todavía está en versión alfa, ¡necesitamos su ayuda para mejorarlo! Para comenzar, aprenda más 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.