Resumen de las rutinas de Kotlin y su uso en Android (3. Reescribe las devoluciones de llamada y las llamadas de RxJava en funciones de suspensión)

Inserte la descripción de la imagen aquí
Este artículo presenta principalmente lo siguiente, para la lógica de devolución de llamada de interfaz en el proyecto existente, y el código de lógica de suscripción en Rxjava, modificado en la forma de función de suspensión suspendida.

1 Reescritura de la devolución de llamada de la interfaz

En el escenario de devolución de llamada de interfaz general, todos podemos cambiar la forma de suspender funciones, como las devoluciones de llamada de éxito y falla de la interfaz de red, la devolución de llamada de la aplicación para permisos dinámicos u otras interfaces de devolución de llamada asíncronas.

(1) Use suspendCoroutine

Tomando la solicitud de red Llamar en OkHttp como ejemplo, consulte el Análisis completo de corotinas de Kotlin (Corutinas) (3), encapsulando devoluciones de llamada asíncronas, la relación entre las corutinas y la cancelación de las mismas , podemos agregar una función de extensión al objeto Llamarawait() , el código es el siguiente:

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)
        } 
    })
}

Es decir, mediante suspendCoroutineo suspendCancellableCoroutine, utilizando su interior Continuationo CancellableContinuationel resumemétodo de recuperación para co-rutina normal, o por resumeWithExceptionerror anormal para lograr los principios de la devolución de llamada.

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()
    }

La idea de transformarse en una función de suspensión también es muy simple, es decir, una capa se encapsula en la devolución de llamada de éxito y error de la interfaz original, de modo que la persona que llama puede utilizar la forma de rutina para reducir el anidamiento de las interfaces.

(2) 使用 suspenderCancellableCorutina

suspendCoroutineEl uso introducido anteriormente , ahora introduce suspendCancellableCoroutineel uso, la diferencia entre los dos es que suspendCancellableCoroutinepuedes Job.cancel()cancelar (lanzará CancellationException), el objeto suspendCancellableCoroutinedevuelto CancellableContinuation, podemos llamarlo resume、resumeWithException和抛出CancellationExceptionpara tratar con la lógica de rutina.

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 La devolución de llamada de suscripción de RxJava se convierte en una función de suspensión

La mayoría de los proyectos ahora usan RxJava. Aunque muchos textos promueven el uso de corutinas para reemplazar a RxJava, la API de la rutina todavía está evolucionando. Por otro lado, el código actual de RxJava todavía es aceptable. El costo total de reemplazo Y el riesgo es alto, por lo que usar los dos juntos es una opción óptima.

Para facilitar la conversión de llamadas RxJava a la función de suspensión de las corutinas , jetbrains oficialmente dio una implementación, es decir, usa kotlinx-coroutines-rx2:

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

Todos los constructores de corutina
Tomando el uso de Single como ejemplo, llamar a Single.await () puede ayudarnos a convertir RxJava en uno suspendCancellableCoroutine. El proceso es en realidad el mismo que la conversión anterior. El código fuente es el siguiente:
Inserte la descripción de la imagen aquí
ejemplo de uso:

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

Es decir, nuestro método original no necesita ser modificado, solo use kotlinx-coroutines-rx2la función de espera definida en el lugar de la llamada , suspenderá la ejecución de la rutina actual y luego reanudará la ejecución de la rutina después de que RxJava devuelva el resultado. Por supuesto, no olvide agregar excepción try-catch cuando llame.

Para Maybe y Observable en RxJava, kotlinx-coroutines-rx2se nos proporcionan las funciones de extensión correspondientes. ¡Date prisa y pruébalo en tu código!

Inserte la descripción de la imagen aquí

Resumen:

La función de devolución de llamada o llamadas en RxJava en corrutina suspender llamada a la función, el principio es bastante simple, en suspendCoroutiney suspendCancellableCoroutineprincipios,
que son la clave para llamar a la suspendCoroutineUninterceptedOrReturn()función, su papel es el de obtener una instancia de la co-rutina actual y cuelgue Inicie la rutina actual o devuelva el resultado sin suspenderlo.

También hay dos funciones de suspensión comunes utilizadas en las rutinas suspendCoroutineUninterceptedOrReturn(), a saber, delay()y yield().
delay()Todo el mundo ya está familiarizado con él, yield()es decir, renunciar al poder de ejecución de la corutina actual y dárselo a otras corutinas para su ejecución, lo que puede lograr un efecto similar a la ejecución alterna. Todavía no he pensado en el escenario aplicable.

El análisis de código fuente específico puede referirse al primer artículo al final del artículo.

Al llamar a otras funciones de suspensión en un Scope, aún necesitamos agregar try-catch(注意应该加在协程体内部,在整个Scope上包try-catch是无法捕获异常的)o usar la CoroutinesExceptionHandlerlógica de procesamiento. Aunque Kotlin afirma que no hay excepciones comprobadas , pero para la lógica del proceso y la operación normal del código, todavía Procesamiento de captura en la llamada.

Referencia:
Kotlin Coroutines (Coroutines) Análisis completo (3), encapsulando devoluciones de llamada asincrónicas, relaciones entre coroutinas y cancelación de
coroutinas.

82 artículos originales publicados · Me gusta 86 · Visita 110,000+

Supongo que te gusta

Origin blog.csdn.net/unicorn97/article/details/105163021
Recomendado
Clasificación