Corotine Kotlin et résumé d'utilisation dans Android (deux composants d'architecture Jetpack utilisés ensemble)

Insérez la description de l'image ici

Sous Android, il est officiellement recommandé d'utiliser avec les composants de l'architecture Jetpack

(1) Introduction des coroutines

  • implémentation 'org.jetbrains.kotlinx: kotlinx-coroutines-android: 1.3.3'

(2) Introduire l'extension KTX des composants de l'architecture Jetpack

  • Pour ViewModelScope, veuillez utiliser androidx.lifecycle: lifecycle-viewmodel-ktx: 2.1.0-beta01 ou supérieur.
  • Pour LifecycleScope, veuillez utiliser androidx.lifecycle: lifecycle-runtime-ktx: 2.2.0-alpha01 ou supérieur.
  • Pour liveData, veuillez utiliser androidx.lifecycle: lifecycle -ivedata-ktx: 2.2.0-alpha01 ou supérieur.

ViewModelScope

Chaque ViewModel de l'application définit un ViewModelScope. Si le ViewModel est effacé, les coroutines démarrées dans cette plage seront automatiquement annulées. Si vous avez du travail à faire uniquement lorsque le ViewModel est actif, les coroutines sont très utiles pour le moment. Par exemple, si vous souhaitez calculer certaines données pour la mise en page, vous devez limiter la portée du travail au ViewModel, afin qu'après que le ViewModel soit effacé, le système annule automatiquement le travail pour éviter de consommer des ressources.

class MyViewModel: ViewModel() {
        init {
            viewModelScope.launch {
                // Coroutine that will be canceled when the ViewModel is cleared.
            }
        }
    }

LifecycleScope

LifecycleScope est défini pour chaque objet Lifecycle. Les coroutines commencées dans cette plage seront annulées lorsque le cycle de vie sera détruit. Vous pouvez accéder à CoroutineScope of Lifecycle via la propriété lifecycle.coroutineScope ou lifecycleOwner.lifecycleScope.

L'exemple suivant montre comment utiliser lifecycleOwner.lifecycleScope pour créer un texte précalculé de manière asynchrone:

class MyFragment: Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            viewLifecycleOwner.lifecycleScope.launch {
                val params = TextViewCompat.getTextMetricsParams(textView)
                val precomputedText = withContext(Dispatchers.Default) {
                    PrecomputedTextCompat.create(longTextContent, params)
                }
                TextViewCompat.setPrecomputedText(textView, precomputedText)
            }
        }
    }

Statut du cycle de vie LifecycleScope +

Le cycle de vie propose d'autres méthodes: lifecycle.whenCreated, lifecycle.whenStarted et lifecycle.whenResumed. Si le cycle de vie n'est pas au moins dans l'état minimum requis, toutes les coroutines s'exécutant dans ces blocs seront suspendues.

L'exemple suivant contient des blocs de code qui ne s'exécuteront que si le cycle de vie associé est au moins DÉMARRÉ:

class MyFragment: Fragment {
        init { // Notice that we can safely launch in the constructor of the Fragment.
            lifecycleScope.launch {
                whenStarted {
                    // The block inside will run only when Lifecycle is at least STARTED.
                    // It will start executing when fragment is started and
                    // can call other suspend methods.
                    loadingView.visibility = View.VISIBLE
                    val canAccess = withContext(Dispatchers.IO) {
                        checkUserAccess()
                    }

                    // When checkUserAccess returns, the next line is automatically
                    // suspended if the Lifecycle is not *at least* STARTED.
                    // We could safely run fragment transactions because we know the
                    // code won't run unless the lifecycle is at least STARTED.
                    loadingView.visibility = View.GONE
                    if (canAccess == false) {
                        findNavController().popBackStack()
                    } else {
                        showContent()
                    }
                }

                // This line runs only after the whenStarted block above has completed.

            }
        }
    }

Si le cycle de vie est détruit par une certaine méthode when lorsque la coroutine est active, la coroutine sera automatiquement annulée. Dans l'exemple suivant, une fois que l'état du cycle de vie devient DÉTRUIT, le bloc finally s'exécutera:

class MyFragment: Fragment {
        init {
            lifecycleScope.launchWhenStarted {
                try {
                    // Call some suspend functions.
                } finally {
                    // This line might execute after Lifecycle is DESTROYED.
                    if (lifecycle.state >= STARTED) {
                        // Here, since we've checked, it is safe to run any
                        // Fragment transactions.
                    }
                }
            }
        }
    }

Résumé de l'utilisation de l'état du cycle de vie de LifecycleScope +
通过一种声明式的写法(即在Activity或者Fragment的初始化时,声明好各个生命周期需要做的事情),而不是程序性的写法(即在各个生命周期回调方法中处理相应逻辑),更有利于整体代码结构的把握,更易于理解和维护。所以建议大家多使用这种具有声明式的写法处理业务逻辑。

Utiliser des coroutines avec LiveData

Vous pouvez utiliser la fonction de création de données en direct pour appeler la fonction de suspension et transférer le résultat en tant qu'objet LiveData.

Dans l'exemple suivant, loadUser () est une fonction de pause déclarée ailleurs. Utilisez la fonction liveData builder pour appeler loadUser () de manière asynchrone, puis utilisez emit () pour émettre le résultat:

val user: LiveData<User> = liveData {
        val data = database.loadUser() // loadUser is a suspend function.
        emit(data)
    }

Lorsque LiveData devient actif, le bloc de code commence à s'exécuter; lorsque LiveData devient inactif, le bloc de code est automatiquement annulé après un délai configurable. Si le bloc de code est annulé avant la fin, il redémarrera après que LiveData sera à nouveau actif; s'il se termine avec succès lors de l'exécution précédente, il ne redémarrera pas. Veuillez noter que le bloc de code ne redémarrera que s'il est automatiquement annulé. Si le bloc de code est annulé pour toute autre raison (par exemple, une exception CancerationException est levée), il ne redémarrera pas.

Vous pouvez également émettre plusieurs valeurs à partir du bloc de code. Chaque appel emit () suspend l'exécution du bloc de code:

val user: LiveData<Result> = liveData {
        emit(Result.loading())
        try {
            emit(Result.success(fetchUser()))
        } catch(ioException: Exception) {
            emit(Result.error(ioException))
        }
    }

Vous pouvez également utiliser liveData avec des transformations, comme illustré dans l'exemple suivant:

class MyViewModel: ViewModel() {
        private val userId: LiveData<String> = MutableLiveData()
        val user = userId.switchMap { id ->
            liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
                emit(database.loadUserById(id))
            }
        }
    }

Vous pouvez émettre plusieurs valeurs à partir de LiveData en appelant la fonction emitSource () chaque fois que vous souhaitez émettre une nouvelle valeur. Veuillez noter que chaque appel à emit () ou emitSource () supprimera la source ajoutée précédemment.

class UserDao: Dao {
        @Query("SELECT * FROM User WHERE id = :id")
        fun getUser(id: String): LiveData<User>
    }

    class MyRepository {
        fun getUser(id: String) = liveData<User> {
            val disposable = emitSource(
                userDao.getUser(id).map {
                    Result.loading(it)
                }
            )
            try {
                val user = webservice.fetchUser(id)
                // Stop the previous emission to avoid dispatching the updated user
                // as `loading`.
                disposable.dispose()
                // Update the database.
                userDao.insert(user)
                // Re-establish the emission with success type.
                emitSource(
                    userDao.getUser(id).map {
                        Result.success(it)
                    }
                )
            } catch(exception: IOException) {
                // Any call to `emit` disposes the previous one automatically so we don't
                // need to dispose it here as we didn't get an updated value.
                emitSource(
                    userDao.getUser(id).map {
                        Result.error(exception, it)
                    }
                )
            }
        }
    }

Référence:
Introduction au site officiel Android:
utilisez les coroutines Kotlin pour améliorer les performances des applications,
utilisez les coroutines Kotlin avec des composants d'architecture

A publié 82 articles originaux · J'aime 86 · Plus de 110 000 visiteurs

Je suppose que tu aimes

Origine blog.csdn.net/unicorn97/article/details/104196789
conseillé
Classement