Principes de base d'Android en Kotlin 06.1 : Créer une base de données Room

Cet atelier de programmation fait partie du cours Principes de base d'Android en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.

Introduction

La plupart des applications reposent sur des données qui doivent être conservées, même après que l'utilisateur a fermé l'application. Par exemple, l'application peut stocker une playlist, un inventaire d'éléments de jeu, des tickets de caisse ou des notes de frais, un catalogue de constellations ou des données de sommeil au fil du temps. En général, vous utilisez une base de données pour stocker les données persistantes.

Room est une bibliothèque de base de données qui fait partie d'Android Jetpack. Room se charge de nombreuses tâches liées à la configuration d'une base de données et permet à votre application d'interagir avec en utilisant des appels de fonctions ordinaires. En interne, Room est une couche d'abstraction qui s'ajoute à une base de données SQLite. La terminologie de Room et la syntaxe des requêtes plus complexes suivent le modèle SQLite.

L'image ci-dessous montre comment la base de données Room s'intègre à l'architecture globale recommandée dans ce cours.

Ce que vous devez déjà savoir

Vous devez maîtriser les éléments suivants :

  • Créer une interface utilisateur de base pour une application Android
  • Utiliser des activités, des fragments et des vues.
  • Naviguer entre les fragments et utiliser Safe Args (un plug-in Gradle) pour transmettre des données entre les fragments.
  • Afficher les modèles, les fabriques de modèles et LiveData ainsi que ses observateurs. Ces thèmes liés aux composants d'architecture ont été abordés dans un atelier de programmation précédent de ce cours.
  • Vous disposez de connaissances fondamentales sur les bases de données SQL et le langage SQLite. Pour obtenir un aperçu rapide ou une piqûre de rappel, consultez Principes de base de SQLite.

Points abordés

  • Création d'une base de données Room et interaction avec celle-ci pour conserver les données
  • Comment créer une classe de données qui définit une table dans la base de données.
  • Utilisation d'un objet d'accès aux données (DAO, Data Access Object) pour mapper des fonctions Kotlin à des requêtes SQL
  • Tester le fonctionnement de votre base de données

Objectifs de l'atelier

  • Créez une base de données Room avec une interface pour les données de sommeil nocturnes.
  • Testez la base de données à l'aide des tests fournis.

Dans cet atelier de programmation, vous allez créer la partie base de données d'une application qui suit la qualité du sommeil. L'application utilise une base de données pour stocker les données de sommeil au fil du temps.

L'application comporte deux écrans, représentés par des fragments, comme illustré dans la figure ci-dessous.

Le premier écran, affiché à gauche, comporte des boutons permettant de démarrer et d'arrêter le suivi. L'écran affiche toutes les données de sommeil de l'utilisateur. Le bouton Effacer supprime définitivement toutes les données que l'application a collectées pour l'utilisateur.

Le deuxième écran, à droite, permet de sélectionner une note de qualité du sommeil. Dans l'application, la note est représentée sous forme numérique. À des fins de développement, l'application affiche à la fois les icônes de visages et leurs équivalents numériques.

Voici le parcours de l'utilisateur :

  • L'utilisateur ouvre l'application et l'écran de suivi du sommeil s'affiche.
  • L'utilisateur appuie sur le bouton Démarrer. L'heure de début est enregistrée et affichée. Le bouton Start (Démarrer) est désactivé, et le bouton Stop (Arrêter) est activé.
  • L'utilisateur appuie sur le bouton Arrêter. L'heure de fin est enregistrée et l'écran de qualité du sommeil s'ouvre.
  • L'utilisateur sélectionne une icône de qualité du sommeil. L'écran se ferme et l'écran de suivi affiche l'heure de fin du sommeil et la qualité du sommeil. Le bouton Arrêter est désactivé et le bouton Démarrer est activé. L'application est prête pour une nouvelle nuit.
  • Le bouton Effacer est activé chaque fois que la base de données contient des données. Lorsque l'utilisateur appuie sur le bouton Effacer, toutes ses données sont effacées sans possibilité de retour en arrière. Aucun message de confirmation n'est affiché.

Cette application utilise une architecture simplifiée, comme indiqué ci-dessous dans le contexte de l'architecture complète. L'application n'utilise que les composants suivants :

  • Contrôleur d'interface utilisateur
  • Afficher le modèle et LiveData
  • Une base de données Room

Étape 1 : Téléchargez et exécutez l'application de démarrage

  1. Téléchargez l'application TrackMySleepQuality-Starter depuis GitHub.
  2. Créez et exécutez l'application. L'application affiche l'UI du fragment SleepTrackerFragment, mais aucune donnée. Les boutons ne répondent pas aux commandes tactiles.

Étape 2 : Inspecter l'application de démarrage

  1. Examinez les fichiers Gradle :
  • Le fichier Gradle du projet
     : dans le fichier build.gradle au niveau du projet, notez les variables qui spécifient les versions des bibliothèques. Les versions utilisées dans l'application de démarrage fonctionnent bien ensemble et avec cette application. Lorsque vous aurez terminé cet atelier de programmation, Android Studio pourra vous inviter à mettre à jour certaines versions. C'est à vous de décider si vous souhaitez mettre à jour les versions ou conserver celles qui se trouvent dans l'application. Si vous rencontrez des erreurs de compilation "étranges", essayez d'utiliser la combinaison de versions de bibliothèque utilisée par l'application de solution finale.
  • Fichier Gradle du module. Notez les dépendances fournies pour toutes les bibliothèques Android Jetpack, y compris Room, et les dépendances pour les coroutines.
  1. Examinez les packages et l'UI. L'application est structurée par fonctionnalité. Le package contient des fichiers d'espace réservé dans lesquels vous ajouterez du code tout au long de cette série d'ateliers de programmation.
  • Le package database pour tout le code lié à la base de données Room.
  • Les packages sleepquality et sleeptracker contiennent le fragment, le ViewModel et la fabrique de ViewModel pour chaque écran.
  1. Consultez le fichier Util.kt, qui contient des fonctions permettant d'afficher les données sur la qualité du sommeil. Certains codes sont mis en commentaire, car ils font référence à un ViewModel que vous créerez ultérieurement.
  2. Consultez le dossier androidTest (SleepDatabaseTest.kt). Vous utiliserez ce test pour vérifier que la base de données fonctionne comme prévu.

Dans Android, les données sont représentées dans des classes de données, et elles sont accessibles et modifiées à l'aide d'appels de fonction. Toutefois, dans l'univers des bases de données, vous avez besoin d'entités et de requêtes.

  • Une entité représente un objet ou un concept et ses propriétés, à stocker dans la base de données. Une classe d'entité définit une table, et chaque instance de cette classe représente une ligne de la table. Chaque propriété définit une colonne. Dans votre application, l'entité va contenir des informations sur une nuit de sommeil.
  • Une requête est une demande de données ou d'informations à partir d'une table de base de données ou d'une combinaison de tables, ou une demande d'effectuer une action sur les données. Les requêtes courantes permettent d'obtenir, d'insérer et de mettre à jour des entités. Par exemple, vous pouvez interroger toutes les nuits de sommeil enregistrées, triées par heure de début.

Room fait tout le travail difficile pour vous, des classes de données Kotlin aux entités pouvant être stockées dans des tables SQLite, et des déclarations de fonctions aux requêtes SQL.

Vous devez définir chaque entité comme une classe de données annotée et les interactions comme une interface annotée, un objet d'accès aux données (DAO). Room utilise ces classes annotées pour créer des tables dans la base de données et des requêtes qui agissent sur la base de données.

Étape 1 : Créer l'entité SleepNight

Dans cette tâche, vous allez définir une nuit de sommeil comme une classe de données annotée.

Pour une nuit de sommeil, vous devez enregistrer l'heure de début, l'heure de fin et une note de qualité.

Vous avez également besoin d'un identifiant pour identifier la nuit de manière unique.

  1. Dans le package database, recherchez et ouvrez le fichier SleepNight.kt.
  2. Créez la classe de données SleepNight avec des paramètres pour un ID, une heure de début (en millisecondes), une heure de fin (en millisecondes) et une note numérique de qualité du sommeil.
  • Vous devez initialiser sleepQuality en le définissant sur -1, ce qui indique qu'aucune donnée de qualité n'a été collectée.
  • Vous devez également initialiser l'heure de fin. Définissez-le sur l'heure de début pour indiquer qu'aucune heure de fin n'a encore été enregistrée.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. Avant la déclaration de la classe, annotez la classe de données avec @Entity. Nommez la table daily_sleep_quality_table. L'argument pour tableName est facultatif, mais recommandé. Vous pouvez rechercher d'autres arguments dans la documentation.

    Si vous y êtes invité, importez Entity et toutes les autres annotations de la bibliothèque androidx.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. Pour identifier la propriété nightId comme clé primaire, annotez-la avec @PrimaryKey. Définissez le paramètre autoGenerate sur true pour que Room génère l'ID de chaque entité. L'identifiant de chaque nuit est ainsi forcément unique.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. Annotez les propriétés restantes avec @ColumnInfo. Personnalisez les noms de propriété à l'aide de paramètres, comme indiqué ci-dessous.
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
       @PrimaryKey(autoGenerate = true)
       var nightId: Long = 0L,

       @ColumnInfo(name = "start_time_milli")
       val startTimeMilli: Long = System.currentTimeMillis(),

       @ColumnInfo(name = "end_time_milli")
       var endTimeMilli: Long = startTimeMilli,

       @ColumnInfo(name = "quality_rating")
       var sleepQuality: Int = -1
)
  1. Compilez et exécutez votre code pour vous assurer qu'il ne comporte pas d'erreurs.

Dans cette tâche, vous allez définir un objet d'accès aux données (DAO). Sur Android, le DAO propose des méthodes pratiques pour insérer, supprimer et mettre à jour la base de données.

Lorsque vous utilisez une base de données Room, vous l'interrogez en définissant et en appelant des fonctions Kotlin dans votre code. Ces fonctions Kotlin sont mappées à des requêtes SQL. Vous définissez ces mappages dans un DAO à l'aide d'annotations, et Room crée le code nécessaire.

Considérez un DAO comme une interface personnalisée pour accéder à votre base de données.

Pour les opérations de base de données courantes, la bibliothèque Room fournit des annotations pratiques, telles que @Insert, @Delete et @Update. Pour tout le reste, il y a l'annotation @Query. Vous pouvez écrire n'importe quelle requête prise en charge par SQLite.

En outre, lorsque vous créez vos requêtes SQL dans Android Studio, le compilateur vérifie si une erreur de syntaxe est présente.

Pour la base de données du détecteur de sommeil, vous devez être en mesure d'effectuer les opérations suivantes :

  • Insérez de nouvelles nuits.
  • Modifier une nuit existante : son heure de fin et sa note de qualité
  • Obtenir une nuit spécifique en fonction de sa clé.
  • Obtenir toutes les nuits afin de pouvoir les afficher.
  • Obtenez la nuit la plus récente.
  • Supprimer toutes les entrées de la base de données.

Étape 1 : Créer le DAO SleepDatabase

  1. Dans le package database, ouvrez SleepDatabaseDao.kt.
  2. Notez que interface SleepDatabaseDao est annoté avec @Dao. Tous les DAO doivent être annotés avec le mot clé @Dao.
@Dao
interface SleepDatabaseDao {}
  1. Dans le corps de l'interface, ajoutez une annotation @Insert. Sous @Insert, ajoutez une fonction insert() qui reçoit une instance de la classe Entity appelée SleepNight comme argument.

    Et voilà. Room va maintenant générer tout le code nécessaire pour insérer l'élément SleepNight dans la base de données. Lorsque vous appelez insert() à partir de votre code Kotlin, Room exécute une requête SQL pour insérer l'entité dans la base de données. (Remarque : Vous pouvez donner le nom de votre choix à la fonction.)
@Insert
fun insert(night: SleepNight)
  1. Ajoutez une annotation @Update avec une fonction update() pour un élément SleepNight. L'entité mise à jour est celle qui a la même clé que celle transmise. Vous pouvez mettre à jour tout ou partie des autres propriétés de l'entité.
@Update
fun update(night: SleepNight)

Il n'existe aucune annotation spécifiquement adaptée à la fonctionnalité restante. Vous devez donc utiliser l'annotation @Query et fournir des requêtes SQLite.

  1. Ajoutez une annotation @Query avec une fonction get() qui accepte un argument Long key et renvoie un SleepNight pouvant être nul. Une erreur s'affiche pour indiquer qu'il manque un paramètre.
@Query
fun get(key: Long): SleepNight?
  1. La requête est fournie en tant que paramètre de chaîne à l'annotation. Ajoutez un paramètre à @Query. Fais-en une String qui est une requête SQLite.
  • Sélectionnez toutes les colonnes de daily_sleep_quality_table.
  • Avec WHERE, l'nightId correspond à l'argument :key.

    Notez l'élément :key. Utilisez le caractère deux-points, ce qui permet à la requête de faire référence à des arguments dans la fonction.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. Ajoutez un autre @Query avec une fonction clear() et une requête SQLite pour DELETE tout à partir de daily_sleep_quality_table. Cette requête ne supprime pas la table elle-même.

    L'annotation @Delete supprime un élément. Vous pouvez utiliser @Delete et fournir une liste de nuits à supprimer. L'inconvénient est que vous devez récupérer ou connaître le contenu de la table. L'annotation @Delete est idéale pour supprimer des entrées spécifiques, mais pas pour effacer toutes les entrées d'une table.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. Ajoutez une annotation @Query avec une fonction getTonight(). Rendez le SleepNight renvoyé par getTonight() nullable, afin que la fonction puisse gérer le cas où la table est vide. (La table est vide au début et après l'effacement des données.)

    Pour obtenir "ce soir" à partir de la base de données, écrivez une requête SQLite qui renvoie le premier élément d'une liste de résultats triés par nightId dans l'ordre décroissant. Utilisez LIMIT 1 pour ne renvoyer qu'un seul élément.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. Ajoutez une annotation @Query avec une fonction getAllNights() :
  • Demandez à la requête SQLite de renvoyer toutes les colonnes de la table daily_sleep_quality_table, puis d'effectuer un tri par ordre décroissant.
  • Demandez à getAllNights() de renvoyer la liste des entités SleepNight en tant que LiveData. Room veille automatiquement à ce que LiveData reste à jour. Vous n'avez donc besoin de générer les données de façon explicite qu'une seule fois.
  • Il est possible que vous ayez besoin d'importer LiveData depuis androidx.lifecycle.LiveData.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. Bien qu'aucune modification ne soit visible, exécutez votre application pour vous assurer qu'elle ne comporte pas d'erreurs.

Dans cette tâche, vous allez créer une base de données Room qui utilise les Entity et les DAO créés au cours de la tâche précédente.

Vous devez créer une classe de conteneur de base de données abstraite, annotée avec @Database. Cette classe comporte une méthode qui crée une instance de la base de données si elle n'existe pas ou qui renvoie une référence à une base de données existante.

L'obtention d'une base de données Room est un peu complexe. Voici donc le processus général à suivre avant de commencer à coder :

  • Créez une classe public abstract qui extends RoomDatabase. Cette classe sert de conteneur de base de données. Elle est abstraite, car Room crée l'implémentation à votre place.
  • Annotez la classe avec @Database. Dans les arguments, déclarez les entités de la base de données et définissez le numéro de version.
  • Dans un objet companion, définissez une méthode ou une propriété abstraite qui renvoie un SleepDatabaseDao. Room générera le corps du message pour vous.
  • Vous n'avez besoin que d'une seule instance de la base de données Room pour l'ensemble de l'application. Vous devez donc faire en sorte que RoomDatabase soit un singleton.
  • Utilisez l'outil de création de base de données de Room pour créer la base de données uniquement si elle n'existe pas. Sinon, renvoyez la base de données existante.

Étape 1 : Créer la base de données

  1. Dans le package database, ouvrez SleepDatabase.kt.
  2. Dans le fichier, créez une classe abstract appelée SleepDatabase qui étend RoomDatabase.

    Annotez la classe avec @Database.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. Une erreur s'affiche pour indiquer qu'il manque des paramètres d'entités et de version. Pour que Room puisse créer la base de données, l'annotation @Database doit comporter plusieurs arguments.
  • Fournissez SleepNight en tant que seul élément contenant la liste d'entities.
  • Définissez version sur 1.  Chaque fois que vous modifiez le schéma, vous devez incrémenter le numéro de version.
  • Définissez exportSchema sur false pour ne pas conserver les sauvegardes de l'historique des versions de schéma.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. La base de données doit connaître le DAO. Dans le corps de la classe, déclarez une valeur abstraite qui renvoie SleepDatabaseDao. Vous pouvez avoir plusieurs DAO.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. En dessous, définissez un objet companion. L'objet compagnon permet aux clients d'accéder aux méthodes de création ou d'obtention de la base de données sans instancier la classe. Étant donné que le seul objectif de cette classe est de fournir une base de données, il n'y a aucune raison de l'instancier.
 companion object {}
  1. Dans l'objet companion, déclarez une INSTANCE, une variable privée pouvant être nulle, avant de l'initialiser en lui attribuant la valeur null. La variable INSTANCE conservera une référence à la base de données, si vous en avez créé une. Cela vous permet d'éviter d'ouvrir plusieurs fois des connexions à la base de données, ce qui est coûteux.

Annotez la variable INSTANCE avec @Volatile. La valeur d'une variable volatile ne sera jamais mise en cache, et toutes les écritures et lectures seront effectuées vers et depuis la mémoire principale. Cela permet de s'assurer que la valeur de la variable INSTANCE est toujours à jour et identique pour tous les threads d'exécution. En d'autres termes, les modifications apportées à INSTANCE par un thread sont immédiatement visibles par tous les autres. Vous évitez ainsi, par exemple, que deux threads mettent à jour la même entité dans un cache, ce qui pourrait poser problème.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. En dessous de INSTANCE, tout en restant dans l'objet companion, définissez une méthode getInstance() avec un paramètre Context, dont l'outil de création de base de données aura besoin. Renvoyez un type SleepDatabase. Une erreur s'affiche, car getInstance() ne renvoie rien pour le moment.
fun getInstance(context: Context): SleepDatabase {}
  1. Dans getInstance(), ajoutez un bloc synchronized{}. Transmettez this pour pouvoir accéder au contexte.

    Plusieurs threads peuvent demander une instance de base de données en même temps, ce qui génère deux bases de données au lieu d'une seule. Ce problème est peu susceptible de se produire dans cet exemple d'application, mais il peut survenir dans une application plus complexe. En encapsulant le code pour placer la base de données dans un bloc synchronized, vous vous assurez qu'un seul thread d'exécution à la fois peut y accéder. Ainsi, vous avez la certitude que la base de données n'est initialisée qu'une seule fois.
synchronized(this) {}
  1. Dans le bloc synchronisé, copiez la valeur actuelle de INSTANCE dans une variable locale instance. Cela permet de profiter du smart cast, qui n'est disponible que pour les variables locales.
var instance = INSTANCE
  1. Dans le bloc synchronized, return instance à la fin du bloc synchronized. Ignorez l'erreur de non-concordance du type de retour. Vous ne renverrez jamais la valeur "null" une fois que vous aurez terminé.
return instance
  1. Au-dessus de l'instruction return, ajoutez une instruction if pour vérifier si instance est nul, c'est-à-dire s'il n'y a pas encore de base de données.
if (instance == null) {}
  1. Si instance est défini sur null, utilisez l'outil de création de base de données pour obtenir une base de données. Dans le corps de l'instruction if, appelez Room.databaseBuilder et fournissez le contexte que vous avez transmis, la classe de base de données et un nom pour la base de données, sleep_history_database. Pour supprimer l'erreur, vous devrez ajouter une stratégie de migration et build() au cours des étapes suivantes.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. Ajoutez la stratégie de migration requise au compilateur. Utilisez .fallbackToDestructiveMigration().

    . Normalement, vous devez accompagner toute stratégie de migration d'un objet de migration, au cas où le schéma viendrait à être modifié. Un objet de migration définit la façon dont vous convertissez toutes les lignes de l'ancien schéma pour les rendre compatibles avec le nouveau, de sorte qu'aucune donnée ne soit perdue. La migration dépasse le cadre de cet atelier de programmation. Une solution simple consiste à détruire et à recréer la base de données, ce qui entraîne une perte de données.
.fallbackToDestructiveMigration()
  1. Enfin, appelez .build().
.build()
  1. Attribuez INSTANCE = instance comme dernière étape dans l'instruction if.
INSTANCE = instance
  1. Votre code, une fois fini, doit ressembler à ceci :
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}
  1. Compilez et exécutez votre code.

Vous disposez désormais de tous les éléments nécessaires pour travailler avec votre base de données Room. Ce code peut être compilé et exécuté, mais vous n'avez aucun moyen de savoir s'il fonctionne réellement. C'est donc le moment d'ajouter quelques tests de base.

Étape 2 : Testez SleepDatabase

Au cours de cette étape, vous allez exécuter les tests fournis pour vérifier que votre base de données fonctionne. Cela permet de s'assurer que la base de données fonctionne avant de la développer. Les tests fournis sont basiques. Pour une application de production, vous exécuterez toutes les fonctions et requêtes dans tous les DAO.

L'application de démarrage contient un dossier androidTest. Ce dossier androidTest contient des tests unitaires qui impliquent l'instrumentation Android. En d'autres termes, les tests ont besoin du framework Android. Vous devez donc les exécuter sur un appareil physique ou virtuel. Bien sûr, vous pouvez également créer et exécuter des tests unitaires purs qui n'impliquent pas le framework Android.

  1. Dans Android Studio, dans le dossier androidTest, ouvrez le fichier SleepDatabaseTest.
  2. Pour décommenter le code, sélectionnez tout le code commenté et appuyez sur le raccourci clavier Cmd+/ ou Control+/.
  3. Examinez le fichier.

Voici un aperçu rapide du code de test, car il s'agit d'un autre élément de code que vous pouvez réutiliser :

  • SleepDabaseTest est une classe de test.
  • L'annotation @RunWith identifie le lanceur de test, qui est le programme qui configure et exécute les tests.
  • Lors de la configuration, la fonction annotée avec @Before est exécutée et crée un SleepDatabase en mémoire avec le SleepDatabaseDao. "En mémoire" signifie que cette base de données n'est pas enregistrée dans le système de fichiers et qu'elle sera supprimée après l'exécution des tests.
  • De plus, lors de la création de la base de données en mémoire, le code appelle une autre méthode spécifique aux tests, allowMainThreadQueries. Par défaut, une erreur s'affiche si vous essayez d'exécuter des requêtes sur le thread principal. Cette méthode vous permet d'exécuter des tests sur le thread principal, ce que vous ne devez faire que pendant les tests.
  • Dans une méthode de test annotée avec @Test, vous créez, insérez et récupérez un SleepNight, puis vous affirmez qu'ils sont identiques. En cas de problème, générez une exception. Dans un test réel, vous auriez plusieurs méthodes @Test .
  • Une fois le test terminé, la fonction annotée avec @After s'exécute pour fermer la base de données.
  1. Effectuez un clic droit sur le fichier de test dans le volet Project (Projet), puis sélectionnez Run 'SleepDatabaseTest' (Exécuter "SleepDatabaseTest").
  2. Une fois les tests exécutés, vérifiez dans le volet SleepDatabaseTest que tous les tests ont réussi.

Comme tous les tests ont réussi, vous savez maintenant plusieurs choses :

  • La base de données est créée correctement.
  • Vous pouvez insérer un SleepNight dans la base de données.
  • Vous pouvez récupérer le SleepNight.
  • La valeur de SleepNight est correcte pour la qualité.

Projet Android Studio : TrackMySleepQualityRoomAndTesting

Lorsque vous testez une base de données, vous devez exercer toutes les méthodes définies dans le DAO. Pour terminer les tests, ajoutez et exécutez des tests pour exercer les autres méthodes DAO.

  • Définissez vos tables en tant que classes de données annotées avec @Entity. Définissez les propriétés annotées avec @ColumnInfo en tant que colonnes dans les tables.
  • Définissez un objet d'accès aux données (DAO) en tant qu'interface annotée avec @Dao. Le DAO mappe les fonctions Kotlin aux requêtes de base de données.
  • Utilisez les annotations pour définir les fonctions @Insert, @Delete et @Update.
  • Pour toutes les autres requêtes, utilisez l'annotation @Query avec une chaîne de requête SQLite comme paramètre.
  • Créez une classe abstraite qui possède une fonction getInstance() renvoyant une base de données.
  • Utilisez des tests instrumentés pour vérifier que votre base de données et votre DAO fonctionnent comme prévu. Vous pouvez utiliser les tests fournis comme modèle.

Cours Udacity :

Documentation pour les développeurs Android :

Autres articles et documentation :

Cette section répertorie les devoirs possibles pour les élèves qui suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :

  • Attribuer des devoirs si nécessaire
  • Indiquer aux élèves comment rendre leurs devoirs
  • Noter les devoirs

Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.

Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.

Répondre aux questions suivantes

Question 1

Comment indiquer qu'une classe représente une entité à stocker dans une base de données Room ?

  • Configurez la classe pour qu'elle étende DatabaseEntity.
  • Annotez la classe avec @Entity.
  • Annotez la classe avec @Database.
  • Configurez la classe pour qu'elle étende RoomEntity et annotez-la avec @Room.

Question 2

Le DAO (objet d'accès aux données) est une interface qui permet à Room de mapper les fonctions Kotlin aux requêtes de base de données.

Comment indiquer qu'une interface représente un DAO pour une base de données Room ?

  • Configurez l'interface pour qu'elle étende RoomDAO.
  • Configurez l'interface pour qu'elle étende EntityDao, puis implémentez la méthode DaoConnection().
  • Annotez l'interface avec @Dao.
  • Annotez l'interface avec @RoomConnection.

Question 3

Parmi les affirmations suivantes concernant la base de données Room, lesquelles sont vraies ? Plusieurs réponses sont possibles.

  • Vous pouvez définir des tables pour une base de données Room en tant que classes de données annotées.
  • Si vous renvoyez LiveData à partir d'une requête, Room gardera LiveData à jour pour vous si LiveData change.
  • Chaque base de données Room doit comporter un et un seul DAO.
  • Pour identifier une classe en tant que base de données Room, faites-en une sous-classe de RoomDatabase et annotez-la avec @Database.

Question 4

Quelle annotation pouvez-vous utiliser dans votre interface @Dao ? Plusieurs réponses sont possibles.

  • @Get
  • @Update
  • @Insert
  • @Query

Question 5

Comment vérifier que votre base de données fonctionne ? Plusieurs réponses possibles.

  • En écrivant des tests instrumentés.
  • Continuez à écrire et à exécuter l'application jusqu'à ce qu'elle affiche les données.
  • En remplaçant les appels vers les méthodes dans l'interface DAO par des appels de méthodes équivalentes dans la classe Entity.
  • Exécutez la fonction verifyDatabase() fournie par la bibliothèque Room.

Passer à la leçon suivante : 6.2 Coroutines et Room

Pour obtenir des liens vers d'autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.