mobile-best-practice/chapters/MBP_006_fr.md

5.4 KiB

Optimiser la récupération des données en fonction du cycle de vie

Identifiants

Catégories

Cycle de vie Tiers Responsable
2. Conception Utilisateur/Terminal Developpeur/Développeuse

Indications

Degré de priorité Mise en oeuvre Impact écologique
3 4 3
Ressources Economisées
Mémoire / Réseau

Description

Lorsqu'une personne navigue d'une vue à une autre au sein d'une application, mais également entre plusieurs applications sur son mobile, ces vues transitionnent entre différents états. Selon ces changements d'état, pour optimiser l'usage de la mémoire du téléphone, le système va effectuer des opérations telles que : libérer partiellement la mémoire si l'application passe en arrière-plan, supprimer complétement les données de la mémoire si l'utilisateur quitte la vue en cours en appuyant par exemple sur le bouton retour. Ces changements d'état sont matérialisés par le cycle de vie.

Il convient d'optimiser l'accès aux données utilisées faites par la vue, afin d'éviter d'effectuer des requêtes en base de données locale ou sur le réseau à chaque changement d'état.

Il existe plusieurs bonnes pratiques à appliquer à différents niveaux :

  • À l'échelle de la vue :
    • En utilisant correctement les callbacks du cycle de vie fournies par les composants Activity ou Fragment.
  • À l'échelle du ViewModel :
    • En utilisant une classe de type ViewModel (par exemple via l'implémentation fournie par l'API Jetpack ViewModel) ayant un cycle de vie plus long.

A l'échelle de la vue :

Au sein d'une classe de type Activity ou Fragment, il est courant de surcharger une ou plusieurs méthodes du cycle de vie afin d'initialiser et d'adapter l'état de la vue (via les méthodes onCreate, onResume, onPause,onStop, etc.). Il est important d'éviter de récupérer inutilement les données depuis la base de données locale ou le réseau à chaque fois que la vue redevient visible et utilisable (état RESUMED), c'est-à-dire au sein de la fonction onResume. Il convient plutôt d'initialiser l'état de la vue au sein de la fonction onCreate et de récupérer les données dont elle a besoin soit dans l'état CREATED, soit dans l'état STARTED.

Note : Dans certaines situations, il est conseillé de développer ses propres composants "lifecycle aware" afin d'être notifié de la même façon de ces changements d'états, afin de rendre le code plus maintenable.

A l'échelle du ViewModel

Une classe de type ViewModel a un cycle de vie d'une durée de vie plus longue qu'une Activity ou un Fragment. Gérer et maintenir les données de la vue au sein de ce type de classe permet de les garder en mémoire durant toute la durée de vie de la vue. Typiquement lors des changements de configuration (ex : l'utilisateur change l'orientation de l'écran) ou lorsque la vue passe en arrière-plan. Les données dont la vue a besoin pour se restaurer suite à ces changements restent ainsi accessibles sans avoir besoin d'effectuer une nouvelle requête en base de données ou sur le serveur. L'accès en lecture et en écriture aux données en mémoire est plus rapide et moins consommateur. De plus, une fois que la vue associée au ViewModel est détruit, par exemple si l'utilisateur quitte l'écran en appuyant sur le bouton retour, le ViewModel est aussi détruit, libérant ainsi les données de la mémoire précédemment stockées.

Exemple

Soit une vue d'une application affichant le profil d'un utilisateur :

MainActivity.kt :

class ProfileActivity : ComponentActivity() {

    private val viewModel: ProfileViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Update the uiState
        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collectLatest {
                    // TODO update view
                }
            }
        }
    }
}

ProfileViewModel.kt :


class ProfileViewModel(userRepository: UserRepository) : ViewModel() {

    val uiState: StateFlow<ProfileUIState> = userRepository.userData.map {
        ProfileUIState.Success(it)
    }.stateIn(
        scope = viewModelScope,
        initialValue = ProfileUIState.Loading,
        started = SharingStarted.WhileSubscribed(5_000),
    )
}

sealed interface ProfileUIState {
    data object Loading : ProfileUIState
    data class Success(val user: User) : ProfileUIState
}

Principe de validation

Le nombre ... est inférieur ou égal à
d'écran effectuant des chargements de données à chaque fois que la vue passe dans l'état RESUMED 0%
d'écran rechargeant l'ensemble des données lors d'un changement de configuration 0%
d'écran n'utilisant pas de ViewModel ou équivalent pour maintenir l'état de la vue 0%