Résumé des coroutines Kotlin et de leur utilisation dans Android (3. Réécriture des rappels et des appels RxJava dans les fonctions de suspension)

Insérez la description de l'image ici
Cet article présente principalement les éléments suivants, pour la logique de rappel d'interface dans le projet existant, et le code logique d'abonnement dans Rxjava, modifié sous la forme d'une fonction de suspension suspendue.

1 Réécriture du rappel d'interface

Dans le scénario de rappel d'interface général, nous pouvons tous modifier la forme des fonctions de suspension, telles que les rappels de réussite et d'échec de l'interface réseau, le rappel d'application pour les autorisations dynamiques ou d'autres interfaces de rappel asynchrones.

(1) Utilisez suspendCoroutine

Dans la demande de réseau OkHttp Appel d'exemple, avec référence Kotlin Coroutines (coroutine) entièrement Déterminés (III), désencapsulation asynchrone rappel coroutine inter-relations et coroutine article, nous pouvons appeler ajouter des objets d' une await()fonction d' étalement, comme suit:

suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback<T> {
        override fun onResponse(call: Call<T>, response: Response<T>) { 
            if (response.isSuccessful) {
                cont.Continuation(response.body()!!)
            } else {
                cont.resumeWithException(ErrorResponse(response))
            }
        }
        override fun onFailure(call: Call<T>, t: Throwable) { 
            cont.resumeWithException(t)
        } 
    })
}

À savoir par suspendCoroutineou suspendCancellableCoroutine, en utilisant son interne Continuationou CancellableContinuationle resumeprocédé de récupération de coroutine normale, ou par resumeWithExceptionerreur anormale pour atteindre les principes de la fonction de rappel.

public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =
    suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
        val safe = SafeContinuation(c.intercepted())
        block(safe)
        safe.getOrThrow()
    }

public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T =
    suspendCoroutineUninterceptedOrReturn { uCont ->
        val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
        // 和 suspendCoroutine 的区别就在这里,如果协程已经被取消或者已完成,就会抛出 CancellationException 异常
        cancellable.initCancellability()
        block(cancellable)
        cancellable.getResult()
    }

L'idée de se transformer en fonction de suspension est également très simple, c'est-à-dire qu'une couche est encapsulée sur le succès de l'interface d'origine et le rappel d'erreur, de sorte que l'appelant peut utiliser la forme de coroutine pour réduire l'imbrication des interfaces.

(2) 使用 suspendCancellableCoroutine

suspendCoroutineL'utilisation présentée ci - dessus , introduit maintenant suspendCancellableCoroutinel'utilisation, la différence entre les deux est que suspendCancellableCoroutinevous pouvez Job.cancel()annuler (lancer CancellationException), l' objet suspendCancellableCoroutineretourné CancellableContinuation, nous pouvons l'appeler resume、resumeWithException和抛出CancellationExceptionpour traiter la logique de la coroutine.

MainScope().launch {
  try {
    val user = fetchUser()
    //由于fetchUser中产生来异常,所以下面的updateUser不会被调用到
    updateUser(user)
  } catch (exception: Exception) {
    // Use try-catch or CoroutinesExceptionHandler to handle exceptions.
    Log.d("demo", "$exception") // Prints "java.io.IOException".
  }
  
  // 如果上面使用了try-catch来捕获异常,那么下面的代码仍然可以执行到
  doSomething()
}

// Fetches the user data from server.
private suspend fun fetchUser(): User = suspendCancellableCoroutine { 
cancellableContinuation ->
  fetchUserFromNetwork(object : Callback {
    override fun onSuccess(user: User) {
      cancellableContinuation.resume(user)
    }

    override fun onFailure(exception: Exception) {
      // Invokes this line since the callback onFailure() is invoked.
      cancellableContinuation.resumeWithException(exception)
    }
  })
}

private fun fetchUserFromNetwork(callback: Callback) {
  Thread {
    Thread.sleep(3_000)
    
    //callback.onSuccess(User())
    // Invokes onFailure() callback with "IOException()".
    callback.onFailure(IOException())
  }.start()
}

private fun updateUser(user: User) {
  // Updates UI with [User] data.
}

interface Callback {
  fun onSuccess(user: User)
  fun onFailure(exception: Exception)
}

class User

2 Le rappel d'abonnement de RxJava est converti en fonction de suspension

La plupart des projets utilisent maintenant RxJava. Bien que de nombreux textes encouragent l'utilisation de coroutines pour remplacer RxJava, l'API coroutine évolue toujours. Par contre, le code RxJava actuel est toujours acceptable. Le coût total de remplacement Et le risque est élevé, donc utiliser les deux ensemble est un choix optimal.

Afin de faciliter la conversion des appels RxJava en forme de fonction de suspension des coroutines , jetbrains a officiellement donné une implémentation, c'est-à-dire, utiliser kotlinx-coroutines-rx2:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.2"

Tous les constructeurs de coroutine
Prenant l'exemple de Single, appeler Single.await () peut nous aider à convertir RxJava en un seul suspendCancellableCoroutine. Le processus est en fait le même que la conversion ci-dessus. Le code source est le suivant:
Insérez la description de l'image ici
exemple d'utilisation:

MainScope().launch {
  CoroutineScope(Dispatchers.Main).launch {
     try {
    	val user = fetchUserFromServer().await()
    	updateUser(user)
  	} catch (e: Exception) {
    	Log.d("demo", "(4) {$e}, ${Thread.currentThread()}")
  	}
  }
}

private fun fetchUserFromServer(): Single<User> =
  Single.create<User> {
    Log.d("demo", "(1) fetchUserFromServer start, ${Thread.currentThread()}")
    Thread.sleep(3_000)
    it.onSuccess(User())
    Log.d("demo", "(2) fetchUserFromServer onSuccess, ${Thread.currentThread()}")
  }.subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())

private fun updateUser(user: User) {
  Log.d("demo", "(3) updateUser, ${Thread.currentThread()}")
}

class User

Autrement dit, notre méthode d'origine n'a pas besoin d'être modifiée, utilisez simplement kotlinx-coroutines-rx2la fonction d'attente définie dans la place d'appel , elle suspendra l'exécution de la coroutine actuelle, puis reprendra l'exécution de la coroutine après que RxJava ait renvoyé le résultat. Bien sûr, n'oubliez pas d'ajouter une exception try-catch lors de l'appel.

Pour Maybe et Observable dans RxJava, kotlinx-coroutines-rx2les fonctions d'extension correspondantes sont fournies pour nous. Dépêchez-vous et essayez-le dans votre code!

Insérez la description de l'image ici

Résumé:

La fonction de rappel ou d' appels en RxJava dans coroutine suspendent appel de fonction, le principe est assez simple, sur suspendCoroutineet suspendCancellableCoroutineprincipes,
ils sont la clé pour appeler la suspendCoroutineUninterceptedOrReturn()fonction, son rôle est d'obtenir une instance de la coroutine actuelle et blocage Démarrez la coroutine actuelle ou renvoyez le résultat sans le suspendre.

Il existe également deux fonctions de suspension courantes utilisées dans les coroutines suspendCoroutineUninterceptedOrReturn(), à savoir delay()et yield().
delay()Tout le monde le connaît déjà, yield()c'est-à-dire de renoncer au pouvoir d'exécution de la coroutine actuelle et de le donner à d'autres coroutines pour exécution, ce qui peut produire un effet similaire à une exécution en alternance. Je n'ai pas encore pensé au scénario applicable.

Une analyse de code source spécifique peut faire référence au premier article à la fin de l'article.

Lors de l'appel d'autres fonctions de suspension dans une étendue, nous devons encore ajouter try-catch(注意应该加在协程体内部,在整个Scope上包try-catch是无法捕获异常的)ou utiliser la CoroutinesExceptionHandlerlogique de traitement. Bien que Kotlin affirme qu'il n'y a pas d'exceptions vérifiées , mais pour le traitement logique du processus et le fonctionnement normal du code, nous devons encore Capturez le traitement à l'appel.

Référence:
Kotlin Coroutines (Coroutines) Analyse complète (3), encapsulant les rappels asynchrones, relations entre coroutines et annulation de
coroutines

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/105163021
conseillé
Classement