Want to create interactive content? It’s easy in Genially!

Get started free

Persistencia de datos.

Lidia Hernandez

Created on November 2, 2022

Start designing with a free template

Discover more than 1500 professional designs like these:

Practical Presentation

Smart Presentation

Essential Presentation

Akihabara Presentation

Pastel Color Presentation

Modern Presentation

Relaxing Presentation

Transcript

Persistencia de Datos en aplicaciones móviles: Android.

Profesor: Dr. Ricardo Pérez Águila. Alumna: Lidia Nayelli Hernández Piña. Asignatura: Diseño de aplicaciones Android.

índice

06 Bases de datos: Guardar contenido en una base de datos local con Room.

01 Conceptos.

02 Almacenamiento específico de la app.

07 Conclusiones.

03 Almacenamiento compartido: Acceder a archivos de contenido multimedia

08 Referencias.

04 Acceder a documentos y otros archivos desde el almacenamiento compartido.

05 Preferencias: Shared Preferences y DataStore.

Conceptos

La persistencia de acuerdo con el sitio web Uniwebsidad(2011-2014) es:

" la capacidad de guardar la información de un programa para poder volver a utilizarla en otro momento. " "para un programador puede significar más cosas y suele involucrar un proceso de serialización de los datos a un archivo o a una base de datos o a algún otro medio similar, y el proceso inverso de recuperar los datos a partir de la información serializada."

Conceptos básicos

Base de Datos

Persistencia de Información

Persistencia de OBJETOS

Metadata

Destino de Persistencia

Mecanismo de Persistencia

Mecanismos de persistencia.

Serialización

Acceso directo a bases de datos

Mapeadores Objeto - Relacional

Generadores de Código

Destinos de Persistencia

Archivos:Texto plano, XML, CSV

BASES DE DATOS: RELACIONALES, NO RELACIONALES, ORIENTADAS A OBJETOS, ETC.

Momento de persistencia.

Momento determinado: CUANDO EL USUARIO LO INDIQUE O CADA CIERTO TIEMPO.

Estático: los DATOS SE CARGAN AL INICIAR LA APLICACIÓN Y SE GUARDAN AL FINALIZAR.

Continuo: a MEDIDA EN QUE SON MODIFICADOS.

Opciones de almacenamiento de datos de app en Android.

Almacenamiento específico de la app.

Preferencias.

Bases de datos.

Almacenamiento compartido.

Almacenamiento específico de la app.

Ubicaciones para almacenar los archivos específicos de la app.

Directorios de almacenamiento interno:

  • Incluyen una ubicación orientada a almacenar archivos persistentes y otra ubicación para almacenar datos en caché.
  • El sistema evita que otras apps accedan a las ubicaciones que, en Android 10 (API nivel 29) o versiones posteriores, están encriptadas.
Directorios de almacenamiento externo:
  • Incluyen una ubicación dedicada a fin de almacenar archivos persistentes y otra ubicación para almacenar datos en caché.
  • Aunque es posible que otra app acceda a los directorios si tiene los permisos adecuados, los archivos almacenados allí son para uso exclusivo de tu app.

Operaciones desde el almacenamiento interno I.

Acceder a archivos y almacenarlos.

Java:

Kotlin:

File file = new File(context.getFilesDir(), filename);

val file = File(context.filesDir, filename)

Almacenar un archivo con una transmisión.

Java:

Kotlin:

String filename = "myfile"; String fileContents = "Hello world!"; try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) { fos.write(fileContents.toByteArray()); }

val filename = "myfile" val fileContents = "Hello world!" context.openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) }

Operaciones desde el almacenamiento interno II.

Acceder a archivos y almacenarlos.

Java:

Kotlin:

FileInputStream fis = context.openFileInput(filename); InputStreamReader inputStreamReader = new InputStreamReader(fis, StandardCharsets.UTF_8); StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(inputStreamReader)) { String line = reader.readLine(); while (line != null) { stringBuilder.append(line).append('\n'); line = reader.readLine(); } } catch (IOException e) { // Error occurred when opening raw file for reading. } finally { String contents = stringBuilder.toString(); }

context.openFileInput(filename).bufferedReader().useLines { lines -> lines.fold("") { some, text -> "$some\n$text" } }

Operaciones desde el almacenamiento interno III.

Ver la lista de archivos.

Java:

Kotlin:

Array<String> files = context.fileList();

var files: Array<String> = context.fileList()

Crear directorios anidados.

Java:

Kotlin:

File directory = context.getFilesDir(); File file = new File(directory, filename);

context.getDir(dirName, Context.MODE_PRIVATE) }

Crear archivos de caché.

Java:

Kotlin:

File.createTempFile(filename, null, context.cacheDir)

File.createTempFile(filename, null, context.cacheDir)

Operaciones desde el almacenamiento externo I.

Verificar que el almacenamiento esté disponible.

Kotlin:

Java:

// Checks if a volume containing external storage is available // for read and write. private boolean isExternalStorageWritable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } // Checks if a volume containing external storage is available to at least read. private boolean isExternalStorageReadable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY); }

// Checks if a volume containing external storage is available // for read and write. fun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } // Checks if a volume containing external storage is available to at least read. fun isExternalStorageReadable(): Boolean { return Environment.getExternalStorageState() in setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY) }

Operaciones desde el almacenamiento externo II.

Seleccionar una ubicación de almacenamiento físico.

Kotlin:

Java:

File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null); File primaryExternalStorage = externalStorageVolumes[0];

val externalStorageVolumes: Array<out File> = ContextCompat.getExternalFilesDirs(applicationContext, null) val primaryExternalStorage = externalStorageVolumes[0]

Acceder a archivos persistentes

Kotlin:

Java:

File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);

val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)

Operaciones desde el almacenamiento externo III.

Crear archivos de caché.

Kotlin:

Java:

File externalCacheFile = new File(context.getExternalCacheDir(), filename);

val externalCacheFile = File(context.externalCacheDir, filename)

Quitar archivos de caché.

Kotlin:

Java:

externalCacheFile.delete();

externalCacheFile.delete()

Operaciones desde el almacenamiento externo IV.

Consultar el espacio libre.

Java:

Kotlin:

// App needs 10 MB within internal storage. const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; val storageManager = applicationContext.getSystemService<StorageManager>()!! val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir) val availableBytes: Long = storageManager.getAllocatableBytes(appSpecificInternalDirUuid) if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP) } else { val storageIntent = Intent().apply { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. action = ACTION_MANAGE_STORAGE } }

// App needs 10 MB within internal storage. private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class); UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir()); long availableBytes = storageManager.getAllocatableBytes(appSpecificInternalDirUuid); if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP); } else { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. Intent storageIntent = new Intent(); storageIntent.setAction(ACTION_MANAGE_STORAGE); }

Almacenamiento compartido: Acceder a archivos de contenido multimedia

Acceso a contenido multimedia compartido.

  • Archivos multimedia que se pueden compartir (imágenes, archivos de audio, videos).
  • Para acceder se utiliza la API de MediaStore.
  • Permisos requeridos:
    • READ_EXTERNAL_STORAGE cuando accedes a archivos de otras apps en Android 11 (nivel de API 30) o versiones posteriores.
    • READ_EXTERNAL_STORAGE o WRITE_EXTERNAL_STORAGE cuando accedes a archivos de otras apps en Android 10 (nivel de API 29).
    • Se requieren permisos para todos los archivos en Android 9 (nivel de API 28) o versiones anteriores.

Declaraciones correspondientes para permisos de almacenamiento a archivos multimedia de otras apps.

<!-- Required only if your app needs to access images or photos that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Required only if your app needs to access videos that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- Required only if your app needs to access audio files that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- If your app doesn't need to access media files that other apps created, set the "maxSdkVersion" attribute to "28" instead. --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />

Algunas operaciones con archivos multimedia.

Abrir un archivo multimedia.

Java:

Kotlin:

// Open a specific media item using ParcelFileDescriptor. val resolver = applicationContext.contentResolver // "rw" for read-and-write; // "rwt" for truncating or overwriting existing file contents. val readOnlyMode = "r" resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd -> // Perform operations on "pfd". }

// Open a specific media item using ParcelFileDescriptor. ContentResolver resolver = getApplicationContext() .getContentResolver(); // "rw" for read-and-write; // "rwt" for truncating or overwriting existing file contents. String readOnlyMode = "r"; try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(content-uri, readOnlyMode)) { // Perform operations on "pfd". } catch (IOException e) { e.printStackTrace(); }}

Transmisión de archivos.

Java:

Kotlin:

// Open a specific media item using InputStream. val resolver = applicationContext.contentResolver resolver.openInputStream(content-uri).use { stream -> // Perform operations on "stream". }

// Open a specific media item using InputStream. ContentResolver resolver = getApplicationContext() .getContentResolver(); try (InputStream stream = resolver.openInputStream(content-uri)) { // Perform operations on "stream". }

Acceder a documentos y otros archivos desde el almacenamiento compartido.

Acceso a contenido multimedia compartido.

  • Considera otros tipos de contenido que se puede compartir, incluidos los archivos descargados .
  • Para acceder se utiliza un Framework de acceso a almacenamiento
  • El framework de acceso al almacenamiento admite los siguientes casos prácticos para el acceso a archivos y otros documentos.
    • Crear un archivo nuevo: La acción de intent ACTION_CREATE_DOCUMENT permite a los usuarios guardar un archivo en una ubicación específica.
    • Abrir un documento o archivo: La acción de intent ACTION_OPEN_DOCUMENT permite a los usuarios seleccionar un documento o archivo específico para abrir.
    • Otorgar acceso al contenido de un directorio: La acción de intent ACTION_OPEN_DOCUMENT_TREE, disponible en Android 5.0 (nivel de API 21) y versiones posteriores, permite a los usuarios seleccionar un directorio específico, lo que otorga a la app acceso a todos los archivos y subdirectorios dentro de ese directorio.

Algunas operaciones para a cceder a documentos y otros archivos desde el almacenamiento compartido I.

Crear un archivo nuevo.

Java:

Kotlin:

// Request code for creating a PDF document. const val CREATE_FILE = 1 private fun createFile(pickerInitialUri: Uri) { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "application/pdf" putExtra(Intent.EXTRA_TITLE, "invoice.pdf") // Optionally, specify a URI for the directory that should be opened in // the system file picker before your app creates the document. putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri) } startActivityForResult(intent, CREATE_FILE) }

// Request code for creating a PDF document. private static final int CREATE_FILE = 1; private void createFile(Uri pickerInitialUri) { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("application/pdf"); intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf"); // Optionally, specify a URI for the directory that should be opened in // the system file picker when your app creates the document. intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri); startActivityForResult(intent, CREATE_FILE); }}}

Algunas operaciones para a cceder a documentos y otros archivos desde el almacenamiento compartido II.

Abrir un archivo.

Java:

Kotlin:

// Request code for selecting a PDF document. const val PICK_PDF_FILE = 2 fun openFile(pickerInitialUri: Uri) { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "application/pdf" // Optionally, specify a URI for the file that should appear in the // system file picker when it loads. putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri) } startActivityForResult(intent, PICK_PDF_FILE) }

// Request code for selecting a PDF document. private static final int PICK_PDF_FILE = 2; private void openFile(Uri pickerInitialUri) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("application/pdf"); // Optionally, specify a URI for the file that should appear in the // system file picker when it loads. intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri); startActivityForResult(intent, PICK_PDF_FILE); }}}}

Algunas operaciones para a cceder a documentos y otros archivos desde el almacenamiento compartido II.

Determinar las operaciones que admite un proveedor.

  • Para determinar qué operaciones admite un proveedor determinado, verifica el valor de Document.COLUMN_FLAGS.
  • Cuando la app abre un archivo para leer o escribir, el sistema le otorga un permiso de URI para ese archivo, que dura hasta que se reinicia el dispositivo del usuario.
  • Para conservar el acceso a los archivos después de reiniciar el dispositivo y crear una mejor experiencia del usuario, la app puede "tomar" el permiso de URI persistente que ofrece el sistema, como se muestra en el código:

Kotlin

Java

Preferencias: Shared Preferences y DataStore.

Modos de acceso de Shared Preferences.

MODE_PRIVATE: Sólo nuestra aplicación tiene acceso a estas preferencias. MODE_WORLD_READABLE: Todas las aplicaciones pueden leer estas preferencias, pero sólo la nuestra puede modificarlas (deprecated desde el API 17). MODE_WORLD_WRITEABLE: Todas las aplicaciones pueden leer y modificar estas preferencias (deprecated desde el API 17).

SharedPreferences y DataStore

DataStore

  • Permite almacenar y recuperar datos clave .- valor.
  • Utiliza las coroutinas de Koltin y Flow para poder guardar el dato de forma asíncrona, de forma consistente y transaccional.
  • Se utiliza para guardados de información que no sean muy grandes y que no sean muy complejos.
  • Es posible migrar de SharedPreferences a DataStore.

SharedPreferences

  • Permite almacenar y recuperar en el formato clave-valor información como texto, booleanos y números; lo que lo convierte en potencial para almacenar configuraciones del usuario como: estilos, preferencias, etc.
  • Google indica que tiene mejor perfomance la API DataStore que la de SharedPreferences y que se utilice la primera en vez de la segunda.

+INFO

Shared Preferences

Modos de acceso de Shared Preferences.

MODE_PRIVATE: Sólo nuestra aplicación tiene acceso a estas preferencias. MODE_WORLD_READABLE: Todas las aplicaciones pueden leer estas preferencias, pero sólo la nuestra puede modificarlas (deprecated desde el API 17). MODE_WORLD_WRITEABLE: Todas las aplicaciones pueden leer y modificar estas preferencias (deprecated desde el API 17).

Principales instrucciones para Shared Preferences.

Recuperar preferencia:

SharedPreferences preferencia = getSharedPreferences("MiPreferencia",Context.MODE_PRIVATE);

Obtener la información de la preferencia:

SharedPreferences preferencia = boolean = preferencia.getBoolean("habilitar_imagenes", true);

Guardar o modificar información de la preferencia:

SharedPreferences preferencia = boolean = preferencia.putBoolean("habilitar_imagenes", false);

DataStore.

Preferences Datastore y Proto Datastore.

Preferences Datastore : Almacena y accede a datos mediante claves. Esta implementación no requiere un esquema predefinido ni proporciona seguridad de tipo.

Proto Datastore: Almacena datos como instancias de un tipo personalizado de datos. Esta implementación requiere que definas un esquema con búferes de protocolo, pero proporciona seguridad de tipo.

Configuración de DataStore en archivo de Gradle en Kotlin.

// Preferences DataStore (SharedPreferences like APIs) dependencies { implementation("androidx.datastore:datastore-preferences:1.0.0") // optional - RxJava2 support implementation("androidx.datastore:datastore-preferences-rxjava2:1.0.0") // optional - RxJava3 support implementation("androidx.datastore:datastore-preferences-rxjava3:1.0.0") } // Alternatively - use the following artifact without an Android dependency. dependencies { implementation("androidx.datastore:datastore-preferences-core:1.0.0") }

Configuración de Proto DataStore en archivo de Gradle en Kotlin.

// Typed DataStore (Typed API surface, such as Proto) dependencies { implementation("androidx.datastore:datastore:1.0.0") // optional - RxJava2 support implementation("androidx.datastore:datastore-rxjava2:1.0.0") // optional - RxJava3 support implementation("androidx.datastore:datastore-rxjava3:1.0.0") } // Alternatively - use the following artifact without an Android dependency. dependencies { implementation("androidx.datastore:datastore-core:1.0.0") }

Almacena pares clave-valor con Preferences Datastore: Crea un Preferences Datastore.

Kotlin:

// At the top level of your kotlin file: val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

Java:

RxDataStore<Preferences> dataStore = new RxPreferenceDataStoreBuilder(context, /*name=*/ "settings").build();

Almacena pares clave-valor con Preferences Datastore: Leer un Preferences Datastore.

Kotlin:

val EXAMPLE_COUNTER = intPreferencesKey("example_counter") val exampleCounterFlow: Flow<Int> = context.dataStore.data .map { preferences -> // No type safety. preferences[EXAMPLE_COUNTER] ?: 0 }

Java:

Preferences.Key<Integer> EXAMPLE_COUNTER = PreferencesKeys.int("example_counter"); Flowable<Integer> exampleCounterFlow = dataStore.data().map(prefs -> prefs.get(EXAMPLE_COUNTER));

Almacena pares clave-valor con Preferences Datastore: Escribe en un Preferences Datastore.

Kotlin:

suspend fun incrementCounter() { context.dataStore.edit { settings -> val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0 settings[EXAMPLE_COUNTER] = currentCounterValue + 1 } }

Java:

Single<Preferences> updateResult = dataStore.updateDataAsync(prefsIn -> { MutablePreferences mutablePreferences = prefsIn.toMutablePreferences(); Integer currentInt = prefsIn.get(INTEGER_KEY); mutablePreferences.set(INTEGER_KEY, currentInt != null ? currentInt + 1 : 1); return Single.just(mutablePreferences); }); // The update is completed once updateResult is completed.

Almacena objetos escritos con Proto Datastore. Define un esquema.

Proto Datastore necesita un esquema predefinido en un archivo proto del directorio app/src/main/proto/. Este esquema define el tipo para los objetos que conserves en tu Proto Datastore.

syntax = "proto3"; option java_package = "com.example.application"; option java_multiple_files = true; message Settings { int32 example_counter = 1; }

Almacena objetos escritos con Proto Datastore. Define un esquema.

Proto Datastore necesita un esquema predefinido en un archivo proto del directorio app/src/main/proto/. Este esquema define el tipo para los objetos que conserves en tu Proto Datastore.

syntax = "proto3"; option java_package = "com.example.application"; option java_multiple_files = true; message Settings { int32 example_counter = 1; }

Almacena objetos escritos con Proto Datastore. Crear un Proto Datastore.

Kotlin:

object SettingsSerializer : Serializer<Settings> { override val defaultValue: Settings = Settings.getDefaultInstance() override suspend fun readFrom(input: InputStream): Settings { try { return Settings.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override suspend fun writeTo( t: Settings, output: OutputStream) = t.writeTo(output) } val Context.settingsDataStore: DataStore<Settings> by dataStore( fileName = "settings.pb", serializer = SettingsSerializer )

Almacena objetos escritos con Proto Datastore. Crear un Proto Datastore.

Java:

private static class SettingsSerializer implements Serializer<Settings> { @Override public Settings getDefaultValue() { Settings.getDefaultInstance(); } @Override public Settings readFrom(@NotNull InputStream input) { try { return Settings.parseFrom(input); } catch (exception: InvalidProtocolBufferException) { throw CorruptionException(“Cannot read proto.”, exception); } } @Override public void writeTo(Settings t, @NotNull OutputStream output) { t.writeTo(output); } } RxDataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(context, /* fileName= */ "settings.pb", new SettingsSerializer()).build();

Almacena objetos escritos con Proto Datastore. Crear un Proto Datastore.

Java:

private static class SettingsSerializer implements Serializer<Settings> { @Override public Settings getDefaultValue() { Settings.getDefaultInstance(); } @Override public Settings readFrom(@NotNull InputStream input) { try { return Settings.parseFrom(input); } catch (exception: InvalidProtocolBufferException) { throw CorruptionException(“Cannot read proto.”, exception); } } @Override public void writeTo(Settings t, @NotNull OutputStream output) { t.writeTo(output); } } RxDataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(context, /* fileName= */ "settings.pb", new SettingsSerializer()).build();

Almacena objetos escritos con Proto Datastore. Leer desde un Proto Datastore.

Kotlin:

val exampleCounterFlow: Flow<Int> = context.settingsDataStore.data .map { settings -> // The exampleCounter property is generated from the proto schema. settings.exampleCounter }

Java:

Flowable<Integer> exampleCounterFlow = dataStore.data().map(settings -> settings.getExampleCounter());

Almacena objetos escritos con Proto Datastore. Escribir en un Proto Datastore.

Kotlin:

suspend fun incrementCounter() { context.settingsDataStore.updateData { currentSettings -> currentSettings.toBuilder() .setExampleCounter(currentSettings.exampleCounter + 1) .build() } }

Java:

Single<Settings> updateResult = dataStore.updateDataAsync(currentSettings -> Single.just( currentSettings.toBuilder() .setExampleCounter(currentSettings.getExampleCounter() + 1) .build()));

Bases de datos: Guardar contenido en una base de datos local con Room.

Biblioteca de persistencias Room.

  • Brinda una capa de abstracción para SQLite que permite acceder a la base de datos sin problemas y, al mismo tiempo, aprovechar toda la tecnología de SQLite.
  • Ofrece ventajas como: verificación del tiempo de compilación de las consultas en SQL, anotaciones de conveniencia que minimizan el código estándar repetitivo y propenso a errores y rutas de migración de bases de datos optimizadas.

Configuración en archivo gradle.

Kotlin:

dependencies { val room_version = "2.4.3" implementation("androidx.room:room-runtime:$room_version") annotationProcessor("androidx.room:room-compiler:$room_version") // To use Kotlin annotation processing tool (kapt) kapt("androidx.room:room-compiler:$room_version") // To use Kotlin Symbol Processing (KSP) ksp("androidx.room:room-compiler:$room_version") // optional - Kotlin Extensions and Coroutines support for Room implementation("androidx.room:room-ktx:$room_version") // optional - RxJava2 support for Room implementation("androidx.room:room-rxjava2:$room_version") // optional - RxJava3 support for Room implementation("androidx.room:room-rxjava3:$room_version") // optional - Guava support for Room, including Optional and ListenableFuture implementation("androidx.room:room-guava:$room_version") // optional - Test helpers testImplementation("androidx.room:room-testing:$room_version") // optional - Paging 3 Integration implementation("androidx.room:room-paging:2.5.0-beta01") }

Entidad de datos.

Kotlin:

@Entity data class User( @PrimaryKey val uid: Int, @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )

Java:

@Entity public class User { @PrimaryKey public int uid; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; }

Objeto de acceso a datos (DAO).

Java:

Kotlin:

@Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") fun findByName(first: String, last: String): User @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) }

@@Dao public interface UserDao { @Query("SELECT * FROM user") List<User> getAll(); @Query("SELECT * FROM user WHERE uid IN (:userIds)") List<User> loadAllByIds(int[] userIds); @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") User findByName(String first, String last); @Insert void insertAll(User... users); @Delete void delete(User user); }

Database.

La clase de la base de datos debe cumplir con las siguientes condiciones:

  • La clase debe tener una anotación @Database que incluya un array entities que enumere todas las entidades de datos asociados con la base de datos.
  • Debe ser una clase abstracta que extienda RoomDatabase.
  • Para cada clase DAO que se asoció con la base de datos, esta base de datos debe definir un método abstracto que tenga cero argumentos y muestre una instancia de la clase DAO.

Java:

Kotlin:

@Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }

@Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }

Uso.

Después de definir la entidad de datos, el DAO y el objeto de base de datos, puedes usar el siguiente código para crear una instancia de la base de datos:

Java:

Kotlin:

AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build();

val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build()

Conclusiones.

La persistencia de datos es de gran importancia para cualquier sistema software se trate o no de una aplicación móvil, en este tipo de aplicaciones la decisión de que opción de persistencia utilizar depende en gran medida de factores como:

  • El propósito de la aplicación.
  • El tipo de contenido.
  • La cantidad de información a almacenar.
  • El momento de persistencia.
  • La seguridad de la información que se desea persistir.

¡Gracias!

REFERENCIAS:
  • FING. (s.f.). Persistencia en Sistemas O.O. Recuperado de: https://www.fing.edu.uy/tecnoinf/mvd/cursos/progapli/material/teo/PA-03-Persistencia.pdf
  • Developers Android. (2022). Descripción general del almacenamiento de archivos y datos. Recuperado de: https://developer.android.com/training/data-storage?hl=es-419
  • Uniwebsidad(2011-2014). 11.7. Persistencia de datos. Recuperado de: https://uniwebsidad.com/libros/algoritmos-python/capitulo-11/persistencia-de-datos
  • Developers Android. (2022). DataStore. Recuperado de: https://developer.android.com/topic/libraries/architecture/datastore#kotlin
  • Developers Android. (2022). Cómo guardar contenido en una base de datos local con Room. Recuperado de: https://developer.android.com/training/data-storage/room
  • Developers Android. (2022). Cómo acceder a archivos específicos de la app. Recuperado de: https://developer.android.com/training/data-storage/app-specific?hl=es-419#internal