Introduction à Room — Architecture Components
Room : Exploration de la librairie Android fournissant des outils pour créer, requêter et manipuler plus facilement des bases de données SQLite
Un vent de fraîcheur est passé sur la planète Android au printemps dernier.
En effet, lors de la Google IO 2017, Google a annoncé un ensemble de librairies permettant de produire un code de meilleure qualité, plus robuste et testable plus facilement en comblant certaines lacunes du framework Android : Architecture Components.
Les Architectures Components se divisent en quatre catégories :
LifeCycle : Un set d’APIs permettant de mieux maîtriser les cycles de vie des activités et fragments.
LiveData : Une classe faisant office de wrapper permettant d’observer des changements de données.
ViewModel : Une classe permettant de garder des données ou un état sans qu’elle ne soit impactée par un changement de configuration (rotation de l’écran…)
Enfin Room : Une librairie fournissant des outils pour créer, requêter et manipuler plus facilement des bases de données SQLite. C’est sur cette dernière que nous nous focaliserons dans ce billet, à travers un exemple d’utilisation assez simple.
Comme décrit précédemment, Room est une librairie prenant la forme d’une couche d’abstraction au-dessus d’une base SQLite, permettant ainsi de créer très facilement une base de données, ses tables et de mettre à jour ses données. Dans l’esprit, Room est assez similaire à d’autres ORMs existants sur Android tels que GreenDAO ou encore ORMLite. Si vous avez déjà une expérience avec ces derniers, vous ne devriez pas être dépaysés sur Room.
Configuration
Pour commencer, ajoutez Room à votre projet puis, ajoutez la référence au maven Google à votre fichier build.gradle
, situé au plus haut niveau de votre workspace :
allprojects {
repositories {
maven { url 'https://maven.google.com' }
jcenter()
}
}
Ajoutez ensuite la dépendance à votre build.gradle
au niveau de votre module app :
compile 'android.arch.persistence.room:runtime:1.0.0-alpha9'
compile 'android.arch.persistence.room:rxjava2:1.0.0-alpha9'
kapt "android.arch.persistence.room:compiler:1.0.0-alpha9"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9"
Définition des entités
Dans cet exemple, nous prendrons comme modèle un POJO (ou plutôt un POKO… Vous l’avez, vous l’avez ?) décrivant des news récupérées via un flux RSS :
const val TABLE_NAME = "Articles"
@Entity(tableName = TABLE_NAME)
class Article() : Comparable<Article> {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
var title: String? = null
var date: Date? = null
var description: String? = null
var image: String? = null
var isRead: Boolean = false
override fun compareTo(other: Article): Int {
return other.date!!.compareTo(this.date)
}
constructor(title: String, date: Date, description: String, image: String, id: Long, read: Boolean) : this() {
this.title = title
this.date = date
this.description = description
this.image = image
this.id = id
this.isRead = read
}
}
L’annotation @Entity
signifie que la classe Article correspondra à une table nommée "Articles".
Notons également l’annotation @PrimaryKey
qui désigne la clé primaire de la table, qui sera ici un Long auto généré.
Déclaration des interfaces DAO
Room a ensuite besoin de connaître la façon dont vous désirez récupérer des articles en base. Nous allons pour cela créer une interface ArticleDAO
qui contiendra, entre autres, les requêtes SQL dont vous aurez besoin pour manipuler les données de la table Articles. Car oui, vous aurez toujours à écrire vos requêtes SQL avec Room à la différence près que vous bénéficierez d'un contrôle de la syntaxe à la compilation. En effet, les requêtes étant précompilées au build de l'application, vous ne pourrez pas lancer votre app si votre DAO contient des requêtes erronées.
La déclaration d’un DAO Room se fait grâce à l’annotation @Dao
:
@Dao
interface ArticleDAO {
@Query("Select * FROM Articles")
fun getAllArticles(): Flowable<List<Article>>
@Insert(onConflict = REPLACE)
fun addArticles(article: Article)
@Insert(onConflict = REPLACE)
fun addArticles(articles: List<Article>)
@Delete
fun deleteArticle(article: Article)
@Query("Delete from Articles")
fun deleteAllArticle()
}
Les annotations @Query
désignent les requêtes SQL de l'utilisateur. @Insert
et @Delete
sont utilisées, comme leur nom l'indique, pour insérer ou effacer des lignes dans la table Articles. @Update
est également disponible pour les méthodes visant à mettre à jour des données de la table.
Notons que les requêtes de sélection renvoient par défaut des LiveData [une classe faisant office de wrapper permettant d’observer des changements de données], à savoir des données pouvant être observables suivant les principes de la programmation réactive. Room se mariera donc parfaitement avec RxJava2 ou encore RxKotlin dans votre projet. La première est par ailleurs désormais fournie avec Room comme vous avez dû le remarquer avec les dépendances renseignées en début de billet.
Création de la base de données
Avant de créer votre base de données, vous devez la décrire via une classe héritant de la classe RoomDatabase
.
@Database(entities = arrayOf(Article::class), version = 1)
@TypeConverters(DateTypeAdapter::class)
abstract class RSSReaderDatabase : RoomDatabase() {
abstract fun articleDAO(): ArticleDAO
}
L’annotation @Database
décrit le fait qu'il s'agit d'une base de données Room. Elle permet également de renseigner d'autres attributs tels que la liste des tables, ou encore la version de la base. Cette dernière sera à incrémenter en cas de mise à jour. La méthode articleDAO()
sera le moyen de récupérer un objet ArticleDAO
permettant d'exécuter les requêtes que nous avons décrites dans l'interface éponyme un peu plus haut.
Dans cet exemple, nous stockerons un objet RoomDatabase en tant que singleton, au niveau de la classe Application du projet :
public class RSSReaderApp : Application() {
override fun onCreate() {
super.onCreate()
RSSReaderApp.database = Room.databaseBuilder(this, RSSReaderDatabase::class.java, "articles.db").build();
}
companion object {
var database: RSSReaderDatabase? = null
}
}
Nous sommes à présent prêts à insérer/récupérer des données en base tout simplement en récupérant un objet ArticleDAO
à partir de notre singleton. Comme précisé ci-dessus, nous utiliserons ici RxJava2 afin, d'une part d'être notifié de tout changement sur les données sans refaire une requête, et, d’autre part, de s'assurer que les objets seront émis/insérés dans un nouveau thread. Voici un exemple d'insertion d'une liste d'objets Article
:
Observable.fromCallable {
RSSReaderApp.database?.articleDAO()
?.addArticles(articles)
}
.subscribeOn(Schedulers.newThread())
.subscribe()
Dans l’autre sens, voici à quoi ressemblera la récupération de données en base :
val observable = RSSReaderApp.database?.articleDAO()?.getAllArticles()
?.subscribeOn(Schedulers.newThread())
?.observeOn(AndroidSchedulers.mainThread())
L’émission de nos objets Article
sera alors déclenchée en abonnant un Subscriber
à la variable observable que nous venons de créer :
observable?.subscribe { listArticles -> refreshArticles(listArticles) }
La liste d’articles ainsi récupérée est passée en paramètre à la méthode refreshArticles()
Et c’est tout !
Vous savez désormais créer une base de données, y insérer des données et les récupérer en utilisant Room.
Nous publions régulièrement des articles sur des sujets de développement produit web et mobile, data et analytics, sécurité, cloud, hyperautomatisation et digital workplace.
Suivez-nous pour être notifié des prochains articles et réaliser votre veille professionnelle.
Retrouvez aussi nos publications et notre actualité via notre newsletter, ainsi que nos différents réseaux sociaux : LinkedIn, Twitter, Youtube, Twitch et Instagram
Vous souhaitez en savoir plus ? Consultez notre site web et nos offres d’emploi.
L’auteur
Lead developer Android, Original Raggamuffin