7 pasos para usar la habitación. Tutorial para migrar su aplicación a Room

7 pasos para usar la habitación. Tutorial para migrar su aplicación a Room


Room es una biblioteca que forma parte de los componentes arquitectónicos de Android. Facilita el trabajo con objetos SQLiteDatabase en la aplicación, reduciendo la cantidad de código estándar y verificando consultas SQL en tiempo de compilación.


¿Ya tienes un proyecto de Android que usa SQLite para almacenar datos? Si es así, puede migrarlo a la sala. Veamos cómo tomar un proyecto existente y refactorizarlo para usar Room en 7 simples pasos.


TL; DR: actualice las dependencias de gradle, cree sus entidades, DAO y base de datos, reemplace SQLiteDatabase llamadas SQLiteDatabase con llamadas a métodos DAO, pruebe todo lo que creó o modificó, y elimine las clases no utilizadas. Eso es todo!

En nuestra aplicación de muestra para migración, trabajamos con objetos de tipo User . Utilizamos sabores de productos para demostrar diversas implementaciones de nivel de datos:


  1. sqlite : utiliza SQLiteOpenHelper y las interfaces tradicionales de SQLite.
  2. room : reemplaza la implementación con Room y proporciona migración.

Cada opción utiliza la misma capa de interfaz de usuario, que funciona con la clase UserRepository gracias al patrón MVP.


En la variante sqlite, verá una gran cantidad de código que a menudo se duplica y usa la base de datos en las LocalUserDataSource UsersDbHelper y LocalUserDataSource . Las consultas se crean utilizando ContentValues y los datos devueltos por los objetos del Cursor se leen columna por columna. Todo este código contribuye a la aparición de errores implícitos. Por ejemplo, puede omitir agregar una columna a una consulta o ensamblar incorrectamente un objeto de una base de datos.


Veamos cómo Room mejora nuestro código. Inicialmente, simplemente copiamos las clases de la variante sqlite y las cambiamos gradualmente.


Paso 1. Actualizar las dependencias de gradle


Las dependencias de Room están disponibles a través del nuevo repositorio de Google Maven. Simplemente agréguelo a la lista de repositorios en su archivo build.gradle principal:


 allprojects { repositories { google() jcenter() } } 

Defina la versión de la biblioteca Room en el mismo archivo. Mientras está en la versión alfa, pero esté atento a las actualizaciones de la versión en las páginas del desarrollador :


 ext { roomVersion = '2.1.0-alpha03' } 

En su app/build.gradle agregue dependencias para Room:


 dependencies { implementation “android.arch.persistence.room:runtime:$rootProject.roomVersion” annotationProcessor “android.arch.persistence.room:compiler:$rootProject.roomVersion” androidTestImplementation “android.arch.persistence.room:testing:$rootProject.roomVersion” } 

Para migrar a Room, necesitamos aumentar la versión de la base de datos, y para guardar los datos del usuario, necesitamos implementar la clase Migration . Para probar la migración , necesitamos exportar el esquema. Para hacer esto, agregue el siguiente código al app/build.gradle :


 android { defaultConfig { ... // used by Room, to test migrations javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } } // used by Room, to test migrations sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } ... 

Paso 2. Actualizar clases de modelo a entidades


La sala crea una tabla para cada clase marcada @Entity . Los campos en la clase corresponden a las columnas en la tabla. En consecuencia, las clases de entidad, como regla, son pequeñas clases de modelos que no contienen ninguna lógica. Nuestra clase de User representa un modelo para datos en una base de datos. Entonces, actualicémoslo para decirle a Room que debería crear una tabla basada en esta clase:


  • @Entity la clase con @Entity y use la propiedad tableName para establecer el nombre de la tabla.
  • Establezca la clave principal agregando la anotación @PrimaryKey a los campos correctos; en nuestro caso, esta es la ID de usuario.
  • Especifique el nombre de la columna para los campos de clase utilizando la anotación @ColumnInfo(name = "column_name") . Puede omitir este paso si sus campos ya están nombrados como debe nombrarse la columna.
  • Si hay varios constructores en la clase, agregue la anotación @Ignore para indicarle a Room cuál usar y cuál no.

 @Entity(tableName = "users") public class User { @PrimaryKey @ColumnInfo(name = "userid") private String mId; @ColumnInfo(name = "username") private String mUserName; @ColumnInfo(name = "last_update") private Date mDate; @Ignore public User(String userName) { mId = UUID.randomUUID().toString(); mUserName = userName; mDate = new Date(System.currentTimeMillis()); } public User(String id, String userName, Date date) { this.mId = id; this.mUserName = userName; this.mDate = date; } ... } 

Nota: para una migración sin problemas, preste mucha atención a los nombres de tablas y columnas en la implementación original y asegúrese de configurarlos correctamente en las @ColumnInfo @Entity y @ColumnInfo .


Paso 3. Crear objetos de acceso a datos (DAO)


Los DAO son responsables de definir los métodos de acceso a la base de datos. En la implementación inicial de nuestro proyecto SQLite, todas las consultas de la base de datos se ejecutaron en la clase LocalUserDataSource , donde trabajamos con objetos Cursor . En Room, no necesitamos todo el código asociado con el cursor, y simplemente podemos definir nuestras solicitudes usando anotaciones en la clase UserDao .


Por ejemplo, cuando consulta a todos los usuarios de la base de datos, Room hace todo el "trabajo duro", y solo necesitamos escribir:


 @Query(“SELECT * FROM Users”) List<User> getUsers(); 

Paso 4. Creando una base de datos


Ya hemos definido nuestra tabla de Users y sus consultas correspondientes, pero aún no hemos creado una base de datos que unirá todos estos componentes de Room. Para hacer esto, necesitamos definir una clase abstracta que extienda RoomDatabase . Esta clase está marcada @Database , que enumera los objetos contenidos en la base de datos y el DAO que accede a ellos. La versión de la base de datos debe aumentarse en 1 en comparación con el valor original, por lo que en nuestro caso será 2.


 @Database(entities = {User.class}, version = 2) @TypeConverters(DateConverter.class) public abstract class UsersDatabase extends RoomDatabase { private static UsersDatabase INSTANCE; public abstract UserDao userDao(); 

Como queremos guardar los datos del usuario, necesitamos implementar la clase de Migration dice a Room qué debe hacer al cambiar de la versión 1 a la 2. En nuestro caso, dado que el esquema de la base de datos no ha cambiado, simplemente proporcionamos una implementación vacía:


 static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { //     ,      . } }; 

Cree un objeto de base de datos en la clase UsersDatabase definiendo el nombre de la base de datos y la migración:


 database = Room.databaseBuilder(context.getApplicationContext(), UsersDatabase.class, "Sample.db") .addMigrations(MIGRATION_1_2) .build(); 

Para obtener más información sobre cómo implementar la migración de la base de datos y cómo funcionan bajo el capó, consulte esta publicación .


Paso 5. Actualizar el repositorio para usar Room


Creamos nuestra base de datos, nuestra tabla de usuarios y consultas, por lo que ahora es el momento de usarlas. En este punto, actualizaremos la clase LocalUserDataSource para usar los métodos UserDao . Para hacer esto, primero actualizamos el constructor: eliminar Context y agregar UserDao . Por supuesto, cualquier clase que cree una instancia de LocalUserDataSource también debe actualizarse.


A continuación, actualizaremos los métodos LocalUserDataSource que realizan consultas llamando a los métodos UserDao . Por ejemplo, un método que solicita a todos los usuarios ahora se ve así:


 public List<User> getUsers() { return mUserDao.getUsers(); } 

Y ahora es el momento de lanzar lo que hicimos.


Una de las mejores características de Room es que si realiza operaciones de base de datos en el hilo principal, su aplicación se bloqueará con el siguiente mensaje de error:


 java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. 

Una forma confiable de mover las operaciones de E / S desde el subproceso principal es crear un nuevo Runnable que creará un nuevo subproceso para cada consulta de la base de datos. Como ya usamos este enfoque en la variante sqlite, no se requirieron cambios.


Paso 6. Prueba en el dispositivo


Creamos nuevas clases: UserDao y UsersDatabase y cambiamos nuestra LocalUserDataSource para usar la base de datos de Room. Ahora tenemos que probarlos.


Prueba de usuario


Para probar UserDao , necesitamos crear una clase de prueba AndroidJUnit4 . La característica sorprendente de Room es la capacidad de crear una base de datos en la memoria. Esto elimina la necesidad de limpieza después de cada prueba.


 @Before public void initDb() throws Exception { mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabase.class) .build(); } 

También debemos asegurarnos de cerrar la conexión de la base de datos después de cada prueba.


 @After public void closeDb() throws Exception { mDatabase.close(); } 

Por ejemplo, para probar el inicio de sesión de un usuario, agregamos un usuario y luego verificamos si podemos obtener ese usuario de la base de datos.


 @Test public void insertAndGetUser() { //      mDatabase.userDao().insertUser(USER); //        List<User> users = mDatabase.userDao().getUsers(); assertThat(users.size(), is(1)); User dbUser = users.get(0); assertEquals(dbUser.getId(), USER.getId()); assertEquals(dbUser.getUserName(), USER.getUserName()); } 

Probar el uso de UserDao en LocalUserDataSource


Asegurarse de que LocalUserDataSource siga funcionando correctamente es fácil, ya que tenemos pruebas que describen el comportamiento de esta clase. Todo lo que necesitamos hacer es crear una base de datos en la memoria, obtener un objeto UserDao de ella y usarla como parámetro para el constructor LocalUserDataSource .


 @Before public void initDb() throws Exception { mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabase.class) .build(); mDataSource = new LocalUserDataSource(mDatabase.userDao()); } 

Nuevamente, debemos asegurarnos de cerrar la base de datos después de cada prueba.


Prueba de migración de base de datos


Puede leer más sobre cómo implementar pruebas de migración de bases de datos, así como sobre cómo funciona MigrationTestHelper , en esta publicación .


También puede ver el código de un ejemplo de aplicación de migración más detallado.


Paso 7. Eliminar todo lo innecesario


Elimine las clases y líneas de código no utilizadas que ahora se reemplazan por la funcionalidad de Room. En nuestro proyecto, solo necesitamos eliminar la clase UsersDbHelper , que extendió la clase SQLiteOpenHelper .


Si tiene una base de datos más grande y compleja, y desea cambiar gradualmente a Room, le recomendamos esta publicación .


Ahora, la cantidad de código estándar propenso a errores ha disminuido, las solicitudes se verifican en el momento de la compilación y todo se prueba. En 7 simples pasos, pudimos migrar nuestra aplicación existente a Room. Puede ver una aplicación de ejemplo aquí .

Source: https://habr.com/ru/post/441934/


All Articles