7 étapes pour utiliser Room. Procédure pas à pas pour la migration de votre application vers Room

7 étapes pour utiliser Room. Procédure pas à pas pour la migration de votre application vers Room


Room est une bibliothĂšque qui fait partie des composants architecturaux d' Android. Il facilite l' SQLiteDatabase objets SQLiteDatabase dans l'application, rĂ©duisant la quantitĂ© de code standard et vĂ©rifiant les requĂȘtes SQL au moment de la compilation.


Vous avez déjà un projet Android qui utilise SQLite pour stocker des données? Si tel est le cas, vous pouvez le migrer vers Room. Voyons comment prendre un projet existant et le refactoriser pour utiliser Room en 7 étapes simples.


TL; DR: mettez à jour les dépendances de gradle, créez vos entités, DAO et base de données, remplacez les appels SQLiteDatabase appels de méthode DAO, testez tout ce que vous avez créé ou modifié et supprimez les classes inutilisées. C'est tout!

Dans notre exemple d'application pour la migration, nous travaillons avec des objets de type User . Nous avons utilisé des versions de produit pour démontrer diverses implémentations au niveau des données:


  1. sqlite - utilise SQLiteOpenHelper et les interfaces SQLite traditionnelles.
  2. room - remplace l'implémentation par Room et permet la migration.

Chaque option utilise la mĂȘme couche d'interface utilisateur, qui fonctionne avec la classe UserRepository grĂące au modĂšle MVP.


Dans la variante sqlite, vous verrez beaucoup de code qui est souvent dupliquĂ© et utilise la base de donnĂ©es dans les LocalUserDataSource UsersDbHelper et UsersDbHelper . Les requĂȘtes sont ContentValues aide de ContentValues et les donnĂ©es renvoyĂ©es par les objets Cursor sont lues colonne par colonne. Tout ce code contribue Ă  l'apparition d'erreurs implicites. Par exemple, vous pouvez ignorer l'ajout d'une colonne Ă  une requĂȘte ou assembler de maniĂšre incorrecte un objet Ă  partir d'une base de donnĂ©es.


Voyons comment Room améliore notre code. Au départ, nous copions simplement les classes de la variante sqlite et les modifions progressivement.


Étape 1. Mise Ă  jour des dĂ©pendances Gradle


Les dépendances pour Room sont disponibles via le nouveau référentiel Google Maven. Ajoutez-le simplement à la liste des référentiels dans votre fichier build.gradle principal:


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

DĂ©finissez la version de la bibliothĂšque Room dans le mĂȘme fichier. Bien qu'il soit dans la version alpha, mais restez Ă  l'Ă©coute pour les mises Ă  jour de version sur les pages de dĂ©veloppeur :


 ext { roomVersion = '2.1.0-alpha03' } 

Dans votre app/build.gradle ajoutez des dépendances pour 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” } 

Pour migrer vers Room, nous devons augmenter la version de la base de données et pour enregistrer les données utilisateur, nous devons implémenter la classe Migration . Pour tester la migration , nous devons exporter le schéma. Pour ce faire, ajoutez le code suivant au 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()) } ... 

Étape 2. Mise Ă  jour des classes de modĂšle en entitĂ©s


Room crée une table pour chaque classe marquée @Entity . Les champs de la classe correspondent aux colonnes du tableau. Par conséquent, les classes d'entités sont, en rÚgle générale, de petites classes de modÚles qui ne contiennent aucune logique. Notre classe User représente un modÚle de données dans une base de données. Donc, mettons-le à jour pour indiquer à Room qu'il doit créer une table basée sur cette classe:


  • @Entity la classe avec @Entity et utilisez la propriĂ©tĂ© tableName pour dĂ©finir le nom de la table.
  • DĂ©finissez la clĂ© primaire en ajoutant l'annotation @PrimaryKey aux champs corrects - dans notre cas, il s'agit de l'ID utilisateur.
  • SpĂ©cifiez le nom de colonne pour les champs de classe Ă  l'aide de l'annotation @ColumnInfo(name = "column_name") . Vous pouvez ignorer cette Ă©tape si vos champs sont dĂ©jĂ  nommĂ©s car la colonne doit ĂȘtre nommĂ©e.
  • S'il y a plusieurs constructeurs dans la classe, ajoutez l'annotation @Ignore pour indiquer Ă  Room lequel utiliser et lequel ne pas le faire.

 @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; } ... } 

Remarque: pour une migration en douceur, prĂȘtez une attention particuliĂšre aux noms des tables et des colonnes dans l'implĂ©mentation d'origine et assurez-vous de les dĂ©finir correctement dans les @ColumnInfo @Entity et @ColumnInfo .


Étape 3. CrĂ©ation d'objets d'accĂšs aux donnĂ©es (DAO)


Les DAO sont responsables de la dĂ©finition des mĂ©thodes d'accĂšs Ă  la base de donnĂ©es. Dans l'implĂ©mentation initiale de notre projet SQLite, toutes les requĂȘtes de base de donnĂ©es ont Ă©tĂ© exĂ©cutĂ©es dans la classe LocalUserDataSource , oĂč nous avons travaillĂ© avec des objets Cursor . Dans Room, nous n'avons pas besoin de tout le code associĂ© au curseur, et nous pouvons simplement dĂ©finir nos demandes Ă  l'aide d'annotations dans la classe UserDao .


Par exemple, lors de l'interrogation de tous les utilisateurs de la base de données, Room fait tout le «travail acharné», et nous avons juste besoin d'écrire:


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

Étape 4. CrĂ©ation d'une base de donnĂ©es


Nous avons dĂ©jĂ  dĂ©fini notre table Users et ses requĂȘtes correspondantes, mais nous n'avons pas encore créé de base de donnĂ©es qui rĂ©unira tous ces composants de Room. Pour ce faire, nous devons dĂ©finir une classe abstraite qui Ă©tend RoomDatabase . Cette classe est marquĂ©e @Database , qui rĂ©pertorie les objets contenus dans la base de donnĂ©es et le DAO qui y accĂšdent. La version de la base de donnĂ©es devrait ĂȘtre augmentĂ©e de 1 par rapport Ă  la valeur d'origine, donc dans notre cas, elle sera de 2.


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

Puisque nous voulons enregistrer les données utilisateur, nous devons implémenter la classe Migration indiquant à Room ce qu'il doit faire lors du passage de la version 1 à 2. Dans notre cas, le schéma de la base de données n'ayant pas changé, nous fournissons simplement une implémentation vide:


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

Créez un objet de base de données dans la classe UsersDatabase en définissant le nom de la base de données et la migration:


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

Pour en savoir plus sur la façon d'implémenter la migration de base de données et leur fonctionnement sous le capot, consultez cet article .


Étape 5. Mise Ă  jour du rĂ©fĂ©rentiel pour utiliser Room


Nous avons créé notre base de donnĂ©es, notre table d'utilisateurs et nos requĂȘtes, il est donc temps de les utiliser. À ce stade, nous mettrons Ă  jour la classe UserDao pour utiliser les mĂ©thodes UserDao . Pour ce faire, nous mettons d'abord Ă  jour le constructeur: supprimez le Context et ajoutez UserDao . Bien sĂ»r, toute classe qui crĂ©e une instance de LocalUserDataSource doit Ă©galement ĂȘtre mise Ă  jour.


Ensuite, nous mettrons Ă  jour les mĂ©thodes LocalUserDataSource qui effectuent des requĂȘtes en appelant les mĂ©thodes UserDao . Par exemple, une mĂ©thode qui demande Ă  tous les utilisateurs ressemble maintenant Ă  ceci:


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

Et il est maintenant temps de lancer ce que nous avons fait.


L'une des meilleures fonctionnalités de Room est que si vous effectuez des opérations de base de données sur le thread principal, votre application se bloque avec le message d'erreur suivant:


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

Un moyen fiable de dĂ©placer les opĂ©rations d'E / S du thread principal consiste Ă  crĂ©er un nouveau Runnable qui crĂ©era un nouveau thread pour chaque requĂȘte de base de donnĂ©es. Comme nous utilisons dĂ©jĂ  cette approche dans la variante sqlite, aucune modification n'a Ă©tĂ© requise.


Étape 6. Test sur l'appareil


Nous avons créé de nouvelles classes - UserDao et UsersDatabase et changé notre LocalUserDataSource pour utiliser la base de données Room. Maintenant, nous devons les tester.


Test de UserDao


Pour tester UserDao , nous devons créer une classe de test AndroidJUnit4 . La fonctionnalité étonnante de Room est la possibilité de créer une base de données en mémoire. Cela élimine le besoin de nettoyage aprÚs chaque test.


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

Nous devons également nous assurer que nous fermons la connexion à la base de données aprÚs chaque test.


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

Par exemple, pour tester la connexion d'un utilisateur, nous ajoutons un utilisateur, puis vérifions si nous pouvons obtenir cet utilisateur de la base de données.


 @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()); } 

Test de l'utilisation de UserDao dans LocalUserDataSource


Il est facile de s'assurer que LocalUserDataSource fonctionne toujours correctement, car nous avons déjà des tests qui décrivent le comportement de cette classe. Tout ce que nous devons faire est de créer une base de données en mémoire, d'en extraire un objet UserDao et de l'utiliser comme paramÚtre pour le constructeur LocalUserDataSource .


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

Encore une fois, nous devons nous assurer que nous fermons la base de données aprÚs chaque test.


Test de migration de base de données


Vous pouvez en savoir plus sur la façon d'implémenter des tests de migration de base de données, ainsi que sur le fonctionnement de MigrationTestHelper , dans cet article .


Vous pouvez également voir le code à partir d'un exemple d' application de migration plus détaillé.


Étape 7. Suppression de tout ce qui n'est pas nĂ©cessaire


Supprimez toutes les classes et lignes de code inutilisées qui sont désormais remplacées par la fonctionnalité Room. Dans notre projet, nous avons juste besoin de supprimer la classe UsersDbHelper , qui a étendu la classe SQLiteOpenHelper .


Si vous avez une base de données plus grande et plus complexe et que vous souhaitez passer progressivement à la salle, nous vous recommandons ce poste .


Maintenant, la quantité de code standard sujet aux erreurs a diminué, les demandes sont vérifiées au moment de la compilation et tout est testé. En 7 étapes simples, nous avons pu migrer notre application existante vers Room. Vous pouvez voir un exemple d'application ici .

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


All Articles