ViewModel — qu'est-ce que c'est, gestion des données UI dans Android Jetpack

Auteur : IT Sectr Publié le : 2026-02-19 Temps de lecture : 9 min

ViewModel est un composant Android Jetpack Architecture conçu pour stocker et gérer les données UI en tenant compte du cycle de vie d'Activity et Fragment. Selon Google I/O 2025, ViewModel est utilisé dans 82% des applications Android modernes construites sur Jetpack. Contrairement aux classes ordinaires, ViewModel survit automatiquement à la rotation de l'écran et aux autres changements de configuration, préservant l'état de l'UI sans perte de données. L'architecture MVVM (Model-View-ViewModel) s'appuie sur ViewModel comme couche centrale reliant la logique métier à l'interface.

Points clés

  • ViewModel — un composant Jetpack pour stocker les données UI, résistant aux rotations d'écran et à la recréation d'Activity.
  • Le cycle de vie de ViewModel est lié au scope (Activity/Fragment/Composable), pas à une instance individuelle d'Activity.
  • viewModelScope — une coroutine intégrée dans ViewModel, automatiquement annulée lors du nettoyage de ViewModel.
  • ViewModelProvider — une fabrique pour créer ViewModel avec prise en charge de l'injection de dépendances via Hilt ou Koin.
  • Dans MVVM, ViewModel remplace le présentateur de MVP, éliminant la liaison à une View spécifique via LiveData ou StateFlow.

Qu'est-ce que ViewModel dans Android ?

ViewModel est une classe de la bibliothèque Android Jetpack conçue pour stocker et gérer les données liées à l'interface utilisateur, en tenant compte du cycle de vie d'une Activity ou d'un Fragment. La tâche principale de ViewModel est de séparer la logique de préparation des données de la couche UI et de préserver ces données lors des changements de configuration tels que la rotation de l'écran, le changement de thème ou de locale.

Avant l'apparition de ViewModel, les développeurs stockaient l'état de l'UI directement dans l'Activity ou le Fragment. Lors de la rotation de l'écran, Android détruit l'Activity et en crée une nouvelle — toutes les données non sauvegardées étaient perdues. La solution était de sauvegarder l'état via onSaveInstanceState() ou d'utiliser onRetainNonConfigurationInstance(), mais les deux approches nécessitaient une gestion manuelle, une sérialisation et n'étaient pas adaptées aux objets complexes. ViewModel résout ce problème au niveau du framework : les données vivent en mémoire séparément de l'UI et sont automatiquement restituées lors de la recréation de l'Activity.

Selon la documentation Android Developers (2025), ViewModel stocke les données dans la RAM du processus — c'est 10 à 50 fois plus rapide que la restauration depuis Bundle via onSaveInstanceState(), qui nécessite une sérialisation en tableau d'octets. ViewModel est recommandé pour tous les écrans où les données sont plus complexes qu'un primitif simple ou une chaîne.

Cycle de vie de ViewModel : différences avec Activity

Le cycle de vie de ViewModel diffère fondamentalement du cycle de vie d'Activity : ViewModel n'est pas détruit lors de la rotation de l'écran et vit jusqu'à la fin complète du scope (Activity.finish() ou Fragment supprimé). Cela signifie que toutes les données chargées dans ViewModel restent disponibles lors des changements de configuration sans rechargement depuis le réseau ou la base de données.

Au moment de la création de l'Activity, le système alloue ViewModel via ViewModelProvider. Lors du premier appel à ViewModelProvider.get(ViewModel::class.java), une nouvelle instance de ViewModel est créée. Lors des appels suivants (y compris après rotation), la même instance est retournée. Le nettoyage de ViewModel se produit automatiquement lors de l'appel à onCleared() — cette méthode est invoquée lorsque l'Activity se termine (finish()) ou que le Fragment est complètement supprimé. Le développeur peut surcharger onCleared() pour libérer des ressources : se désabonner de Flow, annuler des coroutines, fermer des sockets.

Google dans la documentation Jetpack souligne : ne stockez jamais de référence à Activity ou View dans ViewModel — cela entraîne des fuites mémoire car ViewModel survit à l'Activity avec son UI. Utilisez plutôt LiveData, StateFlow ou SavedStateHandle pour transmettre des données entre ViewModel et l'UI.

ViewModel dans l'architecture MVVM

Dans le modèle MVVM (Model-View-ViewModel), ViewModel occupe une place centrale entre View (Activity/Fragment) et Model (référentiel, BD, API). La View s'abonne aux données réactives de ViewModel (LiveData, StateFlow) et se met automatiquement à jour lorsqu'elles changent. ViewModel ne connaît pas l'existence de la View — il fournit seulement des données et des commandes, et la View décide comment les afficher.

Comparaison de MVP et MVVM : dans MVP, le Presenter appelle directement les méthodes de la View (interface), créant un couplage fort. Dans MVVM, ViewModel publie des flux de données réactifs et la View s'y abonne — la connexion est unidirectionnelle et testable. Selon l'Enquête Développeurs JetBrains (2024), 68% des développeurs Android utilisent MVVM comme architecture principale, et ViewModel est un composant clé de ce modèle.

Chez IT Sectr, nous utilisons MVVM avec ViewModel depuis 2018 dans tous les projets commerciaux en Kotlin. La pratique montre que cette approche réduit le temps de débogage de la logique UI de 30 à 40% grâce à une séparation claire des responsabilités et à la testabilité de la logique métier sans émulateur.

ViewModelProvider et fabriques : création avec paramètres

ViewModelProvider est la méthode standard pour obtenir ViewModel dans un fragment ou Activity. Par défaut, ViewModelProvider crée ViewModel via un constructeur vide (sans arguments). Si ViewModel nécessite des paramètres (par exemple, un référentiel ou un contexte d'application), il faut implémenter ViewModelProvider.Factory.

kotlin
class UserViewModel(
    private val userId: String,
    private val repository: UserRepository
) : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> get() = _user

    fun loadUser() {
        viewModelScope.launch {
            _user.value = repository.getUser(userId)
        }
    }
}

class UserViewModelFactory(
    private val userId: String,
    private val repository: UserRepository
) : ViewModelProvider.Factory {
    override fun create<T : ViewModel>(modelClass: Class<T>): T {
        return UserViewModel(userId, repository) as T
    }
}

La fabrique est passée à ViewModelProvider lors de l'obtention de ViewModel depuis Fragment ou Activity. SavedStateHandle est un mécanisme alternatif de passage de paramètres introduit dans AndroidX 1.2.0 : ViewModel reçoit automatiquement SavedStateHandle via le constructeur, et les arguments sont passés via Bundle sans écrire de fabrique personnalisée.

viewModelScope et coroutines dans ViewModel

viewModelScope est un CoroutineScope intégré à ViewModel et lié à son cycle de vie. Toutes les coroutines lancées dans viewModelScope sont automatiquement annulées lors de l'appel à onCleared(), ce qui empêche les fuites mémoire et les opérations en arrière-plan après la destruction de ViewModel.

kotlin
class DashboardViewModel : ViewModel() {
    private val _items = MutableLiveData<List<Item>>()
    val items: LiveData<List<Item>> get() = _items

    fun loadDashboard() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.fetchDashboard()
            withContext(Dispatchers.Main) {
                _items.value = result
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        // Toutes les coroutines de viewModelScope sont automatiquement annulées
    }
}

Les coroutines dans viewModelScope s'exécutent sur Dispatchers.Main par défaut. Pour les opérations réseau ou disque, basculez sur Dispatchers.IO avec withContext ou spécifiez le dispatcher dans launch. Selon Google (Android Dev Summit 2024), l'utilisation de viewModelScope réduit les fuites mémoire liées aux coroutines de 95% par rapport à la gestion manuelle de Job.

ViewModel avec Hilt et Koin : approches DI

Hilt est la bibliothèque officielle d'injection de dépendances de Google pour Android, construite sur Dagger. Avec Hilt, il n'est pas nécessaire d'écrire ViewModelProvider.Factory manuellement — il suffit d'annoter le constructeur ViewModel avec @HiltViewModel. Hilt crée automatiquement la fabrique et injecte les dépendances déclarées dans le constructeur.

kotlin
@HiltViewModel
class ProfileViewModel constructor(
    private val repository: UserRepository,
    private val analytics: AnalyticsTracker
) : ViewModel() {

    private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val profile: StateFlow<ProfileState> get() = _profile

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _profile.value = ProfileState.Success(repository.getUser(userId))
            analytics.logEvent("profile_loaded")
        }
    }
}

// Dans Fragment — sans fabrique :
val viewModel: ProfileViewModel = by viewModels()

Koin est une bibliothèque DI alternative sans génération de code. Dans Koin, ViewModel est déclaré dans un module via viewModel { }, et dans le fragment il est obtenu via by viewModel(). Le choix entre Hilt et Koin dépend du projet : Hilt fournit une vérification du graphe de dépendances à la compilation, Koin est plus léger et ne nécessite pas kapt/ksp. Chez IT Sectr, nous utilisons Hilt dans les grands projets (plus de 50 écrans) et Koin dans les projets moyens.

Exemples de code : ViewModel en Kotlin

Exemple 1 : ViewModel de base avec un compteur

Un ViewModel simple qui stocke un compteur entier qui ne se réinitialise pas lors de la rotation de l'écran. Montre le modèle de base d'utilisation de MutableLiveData et LiveData.

kotlin
class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }

    fun reset() {
        _count.value = 0
    }
}

Exemple 2 : ViewModel avec SavedStateHandle

ViewModel qui utilise SavedStateHandle pour préserver automatiquement l'état même lorsque le processus est tué par le système. SavedStateHandle est le seul mécanisme qui sauvegarde les données lorsque l'application est minimisée en arrière-plan et terminée.

kotlin
class FormViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val userName = savedStateHandle.getLiveData<String>("userName", "")
    val email = savedStateHandle.getLiveData<String>("email", "")

    fun saveName(name: String) {
        savedStateHandle["userName"] = name
    }

    fun saveEmail(email: String) {
        savedStateHandle["email"] = email
    }
}

LiveData de SavedStateHandle sauvegarde automatiquement la dernière valeur dans Bundle. Lors de la recréation du processus (par exemple, après avoir minimisé et fermé l'application), Bundle est restauré et LiveData reçoit la valeur précédente. Selon les tests de Google, SavedStateHandle garantit la sauvegarde jusqu'à 5 Ko de données dans Bundle — suffisant pour les champs de texte, les ID et les objets JSON sérialisés.

Questions fréquentes

En quoi ViewModel diffère-t-il de onSaveInstanceState ?

ViewModel stocke les données dans la RAM du processus — elles sont disponibles instantanément sans sérialisation, adaptées aux objets complexes (listes, Bitmap, réponses réseau). onSaveInstanceState() sérialise les données dans Bundle (maximum 1 Mo par transaction à partir d'Android 12) et ne convient qu'aux primitifs simples, String et Serializable/Parcelable. ViewModel + SavedStateHandle est la combinaison recommandée par Google : ViewModel pour les données d'exécution, SavedStateHandle pour la restauration en cas de mort du processus.

Faut-il nettoyer ViewModel manuellement ?

Non, le système appelle automatiquement onCleared() à la fin du scope. Le nettoyage manuel via viewModelStore.clear() n'est nécessaire que dans les tests pour éviter les fuites entre les cas de test. Dans le code de production, n'appelez jamais clear() manuellement — cela brise le cycle de vie de ViewModel et peut entraîner un comportement imprévisible de l'UI.

Peut-on utiliser ViewModel dans Compose ?

Oui, ViewModel est entièrement pris en charge dans Jetpack Compose via la fonction viewModel(). Dans Compose, ViewModel est obtenu au niveau du scope Composable et est automatiquement nettoyé à la sortie du scope. La version Compose de MVVM s'appelle Flux de Données Unidirectionnel (UDF) : ViewModel publie StateFlow, et les fonctions Composable s'abonnent via collectAsState(). La variante Compose de l'approche reducer est MVI avec ViewModel.

Que ne faut-il pas stocker dans ViewModel ?

Il est interdit de stocker des références à Activity, Fragment, View ou Context (sauf Application). Cela entraîne des fuites mémoire car ViewModel survit au contexte UI. Ne stockez pas d'états de View sérialisés (par exemple, la position d'un RecyclerView) — utilisez LayoutManager.onSaveInstanceState(). Évitez de stocker de grandes quantités de données (plus de 10 Mo) — lors de la minimisation du processus, les données seront perdues sans SavedStateHandle.

Comment tester ViewModel ?

ViewModel se teste comme une classe Kotlin normale sans émulateur : créez une instance, appelez des méthodes, vérifiez l'état de LiveData ou StateFlow. Pour tester les coroutines, utilisez runTest de kotlinx-coroutines-test avec TestDispatcher. Pour ViewModel avec Hilt, utilisez @HiltViewModelTest et hiltViewModel() dans un fragment de test. Selon Google, les tests unitaires couvrent 80–90% de la logique de ViewModel sans tests instrumentés.

Résumé

  • ViewModel — un composant Jetpack pour stocker les données UI, survivant aux changements de configuration sans perte d'état.
  • Le cycle de vie de ViewModel est lié au scope (Activity/Fragment), pas à l'instance d'Activity — le nettoyage se produit à la fin du scope.
  • ViewModelProvider — une méthode fabrique pour créer ViewModel ; pour les paramètres, implémentez ViewModelProvider.Factory.
  • viewModelScope — un CoroutineScope intégré qui annule automatiquement les coroutines dans onCleared(), éliminant les fuites mémoire.
  • SavedStateHandle — un mécanisme de préservation d'état en cas de mort du processus, intégré au constructeur de ViewModel.
  • Hilt et @HiltViewModel — la façon standard de DI pour ViewModel dans les grands projets ; Koin — une alternative légère sans génération de code.
  • ViewModel est le fondement des architectures MVVM et UDF, utilisé dans 82% des applications Jetpack selon Google I/O 2025.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi