Les accès à Room dans le thread principal peuvent bloquer l’interface utilisateur.
Cela rend l’application non réactive et peut dégrader l’expérience utilisateur.
L’article explore l’utilisation de coroutines et Flow pour une gestion asynchrone efficace.
Apprenez à construire des applications Android avec une architecture moderne et performante!
Nous allons nous concentrer sur l’utilisation de Room dans les applications Android, en particulier sur l’importance de réaliser les accès à Room dans des threads autres que le thread principal. Nous explorerons pourquoi ces accès doivent être asynchrones et comment les coroutines en Kotlin, ainsi que les Flow, facilitent cette gestion.
Room est une bibliothèque de persistance d’objets relationnels qui simplifie l’interaction avec une base de données SQLite en mappant des objets Java ou Kotlin vers des tables. Les opérations d’accès à la base de données, comme les lectures ou écritures, peuvent être coûteuses en termes de temps, et si elles sont effectuées dans le thread principal, elles peuvent entraîner des blocages de l’interface utilisateur (UI), rendant l’application non réactive.
Accès Room : Problèmes sur Thread Principal
Les accès à Room qui se feraient dans le thread principal sont sanctionnés car ces opérations peuvent être longues et bloquer l’UI. Par conséquent, il est impératif que ces accès soient effectués dans un thread distinct, généralement via des mécanismes d’asynchronisme.
Cependant, bien que les accès à Room doivent être effectués dans un thread séparé, la mise à jour de l’UI doit toujours être réalisée dans le thread principal. Cela pose un défi en matière de synchronisation des threads.
Techniques Anciennes d'Asynchronisme Android
Traditionnellement, pour gérer l’asynchronisme dans les applications Android, diverses techniques ont été utilisées :
- Threads Java :Les threads ont toujours été disponibles dans Java, mais leur gestion peut être complexe et sujette à des erreurs.
- AsyncTask :Cette classe permet de gérer des tâches en arrière-plan et de renvoyer les résultats dans le thread principal. Elle propose des méthodes telles que onPreExecute, doInBackground, et onPostExecute pour gérer l’initialisation, l’exécution en arrière-plan, et le retour des résultats. Toutefois,AsyncTaskest maintenant déprécié en raison de problèmes liés à sa gestion complexe des ressources.
- IntentService :Un service qui s’exécute dans un thread séparé pour accomplir des tâches en arrière-plan. Cependant, cette classe est aussi dépréciée et présente des difficultés similaires à celles d’AsyncTask, notamment le problème de gestion des retours de traitement.
Limites des Techniques Traditionnelles
Les techniques traditionnelles ont des limites majeures :
- Le lancement des tâches se fait dans un endroit, et le retour des résultats dans un autre, ce qui complique la gestion.
- Il y a un risque que les ressources (activités ou fragments) aient disparu au moment où le résultat du traitement est renvoyé, ce qui peut entraîner des erreurs.
Coroutines Kotlin : Accès Room Asynchrone
Kotlin introduit des coroutines et des flows , qui sont des solutions modernes pour gérer l’asynchronisme de manière plus simple et efficace.
Coroutines : Un Code Asynchrone Lisible
Les coroutines permettent d’écrire du code de manière séquentielle, même si certaines parties du code exécutent des opérations bloquantes. Cela rend le code beaucoup plus lisible. Lorsqu’on utilise des coroutines avec Room, le code de lecture ou d’écriture est linéaire, mais s’exécute dans un thread séparé, grâce à l’utilisation de Dispatchers.
Voici un exemple :
class MyViewModel : ViewModel() {
private
val _data =
MutableLiveData>() val data : LiveData> =
_data fun loadData() {
viewModelScope.launch(Dispatchers.IO) {
val plages =
roomDatabase.plageDao().getAllPlages() _data.postValue(plages)
}
}
}
Dans cet exemple :
- viewModelScope.launch lance une coroutine dans le contexte du ViewModel.
- Dispatchers.IO est utilisé pour exécuter la coroutine dans un thread dédié aux entrées/sorties, ce qui est approprié pour les accès à la base de données.
- postValue est utilisé pour mettre à jour l’UI dans le thread principal.
Caractéristique | Description |
---|---|
Simplicité du code | Coroutines simplifient l’écriture du code asynchrone, le rendant plus lisible et facile à maintenir. |
Thread non-bloquant | Les opérations de base de données (lecture/écriture) s’exécutent dans un thread distinct (Dispatchers.IO). |
Exécution linéaire | Bien que les coroutines soient asynchrones, elles permettent d’écrire le code de manière linéaire. |
Gestion automatique des threads | Les coroutines gèrent efficacement l’exécution dans le thread approprié, sans intervention manuelle. |
Flow Kotlin : Données Réactives en Room
Les flows sont utilisés pour émettre des données de manière réactive. En utilisant Flow, les données peuvent être émises par la couche de données (comme Room), et observées par la couche UI, ce qui facilite la gestion des mises à jour dynamiques.
Un exemple typique avec Flow :
class MyViewModel : ViewModel() {
val plagesFlow
: LiveData> =
roomDatabase.plageDao().getAllPlagesFlow().asLiveData() fun
observePlages() {
plagesFlow.observeForever {
plages->
// Mettre à jour l'UI ici
}
}
}
Dans cet exemple, getAllPlagesFlow retourne un Flow qui est observé via LiveData, permettant une mise à jour automatique de l’UI à chaque changement de données.
Exemple : Room avec Coroutines et Flow
Prenons un exemple concret d’une application qui liste des plages dans un Fragment. Nous utilisons un ViewModel pour lancer des coroutines avec un CoroutineScope appelé viewModelScope. Ce scope utilise un Dispatcher dédié aux opérations d’entrée/sortie, comme l’accès aux fichiers ou aux bases de données.
Exemple de Code
Voici un extrait de code qui montre comment une coroutine est utilisée pour accéder aux données via Room :
class PlageViewModel : ViewModel() {
private
val _plages = MutableLiveData>() val plages
: LiveData> =
_plages fun fetchPlages() {
viewModelScope.launch(Dispatchers.IO) {
val plagesList =
roomDatabase.plageDao().getAllPlages() _plages.postValue(plagesList)
}
}
}
- Le ViewModel utilise viewModelScope pour lancer une coroutine.
- Dispatchers.IO est spécifié pour garantir que la lecture de la base de données se fait dans un thread dédié.
- postValue assure que l’UI est mise à jour dans le thread principal une fois que les données sont prêtes.
Gestion des Contextes avec Dispatchers
Dans l’exemple ci-dessus, Dispatchers.IO est utilisé pour spécifier que les opérations doivent être exécutées dans un thread dédié aux entrées/sorties. Si l’on utilise setValue à la place de postValue, il faut explicitement s’assurer que l’opération se fait dans le thread principal, en utilisant Dispatchers.Main.
Conclusion : Optimiser Room avec Kotlin
Nous avons vu l’importance d’utiliser des threads séparés pour les accès à Room afin de préserver la réactivité de l’interface utilisateur. Les coroutines et les flows en Kotlin offrent des solutions modernes et élégantes pour gérer l’asynchronisme. En utilisant ces outils, vous pouvez écrire du code asynchrone de manière séquentielle et lisible, tout en garantissant que les opérations longues ne bloquent pas le thread principal.
Les coroutines simplifient grandement la gestion des threads, tandis que les flows facilitent la gestion réactive des données, assurant ainsi une UI fluide et réactive dans vos applications Android.
Formez-vous gratuitement avec Alphorm !
Maîtrisez les compétences clés en IT grâce à nos formations gratuites et accélérez votre carrière dès aujourd'hui.
FAQ
Pourquoi éviter les accès à Room dans le thread principal?
Quelles sont les limites des techniques traditionnelles d'asynchronisme?
Comment les coroutines facilitent-elles l'accès asynchrone à Room?
Comment utiliser Flow pour la gestion réactive des données?
Comment intégrer Room avec des coroutines dans une application Android?
Conclusion
En adoptant des coroutines et des Flow pour gérer les accès à Room, vous assurez une application plus fluide et réactive. Quels autres outils modernes utilisez-vous pour optimiser vos applications Android?