android retrofit kotlin higher-order-functions

android - Utiliza métodos de reequipamiento de manera más expresiva



retrofit kotlin (5)

Así es como lo hago con la función de extensión y una clase.

fun<T> Call<T>.enqueue(callback: CallBackKt<T>.() -> Unit) { val callBackKt = CallBackKt<T>() callback.invoke(callBackKt) this.enqueue(callBackKt) } class CallBackKt<T>: Callback<T> { var onResponse: ((Response<T>) -> Unit)? = null var onFailure: ((t: Throwable?) -> Unit)? = null override fun onFailure(call: Call<T>, t: Throwable) { onFailure?.invoke(t) } override fun onResponse(call: Call<T>, response: Response<T>) { onResponse?.invoke(response) } }

entonces puedes usarlo así

request.enqueue { onResponse = { // do } onFailure = { // do } }

Quiero void enqueue(Callback<T> callback); método de invocación código de bloque más expresivo, aquí es lo que tengo un generalmente

request.enqueue(object : Callback<MyModel> { override fun onFailure(call: Call<MyModel>?, t: Throwable?) { // } override fun onResponse(call: Call<MyModel>?, response: Response<MyModel>?) { // } })

Y lo que quiero y quiero decir es que, para cambiar este código, se bloquean de manera más limpia y se eliminan las palabras clave de cancelación, el objeto, la devolución de llamada y hacer algo así:

request.enqueue({throwable, response -> })

Creo que podría mejorarse de alguna manera usando extensiones y funciones de orden superior. ¿Alguien sabe cómo se puede hacer?


Dada la siguiente función:

fun <T> callback(fn: (Throwable?, Response<T>?) -> Unit): Callback<T> { return object : Callback<T> { override fun onResponse(call: Call<T>, response: retrofit2.Response<T>) = fn(null, response) override fun onFailure(call: Call<T>, t: Throwable) = fn(t, null) } }

Puedes usar esto con Retrofit de esta manera:

request.enqueue(callback({ throwable, response -> response?.let { callBack.onResponse(response.body() ?: RegisterResponse()) } throwable?.let { callBack.onFailed(throwable.message!!) })

Alternativamente, puede definir esta otra versión de devolución de llamada:

fun <T> callback2(success: ((Response<T>) -> Unit)?, failure: ((t: Throwable) -> Unit)? = null): Callback<T> { return object : Callback<T> { override fun onResponse(call: Call<T>, response: retrofit2.Response<T>) { success?.invoke(response) } override fun onFailure(call: Call<T>, t: Throwable) { failure?.invoke(t) } } }


que se puede utilizar de esta manera:

request.enqueue(callback2( { r -> callBack.onResponse(r.body()) }, { t -> callBack.onFailed(t.message) }))


Lo que puedes hacer es esto (esto es Java, ya que no conozco mucho Kotlin, pero debería ser bastante similar):

public class CallbackWrapper<T> implements Callback<T> { private Wrapper<T> wrapper; public CallbackWrapper(Wrapper<T> wrapper) { this.wrapper = wrapper; } public void onFailure(Call<T> call, Throwable t) { wrapper.onResult(t, null); } public void onResponse(Call<T> call, Response<T> response) { wrapper.onResult(null, response); } public static interface Wrapper<T> { void onResult(Throwable t, Response<T> response); } }

Que puedes usar como:

call.enqueue(new CallbackWrapper((throwable, reponse) - > {...}));

actualización de la solución para kotlin:

Basado en this , el CallBackWrapper ve así:

typealias wrapper<T> = (t: Throwable?, response: Response<T>?) -> Unit class CallbackWrapper<T>(val wrapper: wrapper<T>) : Callback<T> { override fun onFailure(call: Call<T>?, t: Throwable?) = wrapper.invoke(t,null) override fun onResponse(call: Call<T>?, response: Response<T>?) = wrapper.invoke(null, response) }

y usarlo igual que Java.


Puedes crear una función de extensión como esta

inline fun <T> Call<T>.addEnqueue( crossinline onSuccess: (response: Response<T>) -> Unit = { response: Response<T> -> }, crossinline onFail: (t: Throwable) -> Unit = { throwable: Throwable ->} ):Callback<T> { val callback = object : Callback<T> { override fun onFailure(call: Call<T>, t: Throwable) { onFail(t) } override fun onResponse(call: Call<T>, response: Response<T>) { onSuccess(response) } } enqueue(callback) return callback }

Y utilízalo así en tu actividad o fragmento.

service?.fetchUser()?.addEnqueue( onSuccess = { doOnSuccess(it) }, onFail = { doOnFail(it) } )


Utilicé una función de extensión en Call para escribir un método de enqueue expresivo y genérico.

fun<T> Call<T>.onEnqueue(actOnSuccess: (Response<T>) -> Unit, actOnFailure: (t: Throwable?) -> Unit) { this.enqueue(object: Callback<T> { override fun onFailure(call: Call<T>?, t: Throwable?) { actOnFailure(t) } override fun onResponse(call: Call<T>?, response: Response<T>) { actOnSuccess(response) } }) }

Esto puede ser utilizado como:

request.onEnqueue { actOnSuccess = { doOnSuccess() } actOnFailure = { doOnFailure() } }

Dentro de los bloques de código actOnSuccess y actOnFailure, it referirá a los objetos Response y Throwable respectivamente, y se puede aprovechar en consecuencia. Por ejemplo, dentro del bloque de código actOnFailure -

actOnFailure = { doOnFailure() //it.message //''it'' refers to the Throwable object. }