Cómo usar un objeto ListenableFuture

Un ListenableFuture representa el resultado de un procesamiento asíncrono: un que puede o no haber terminado de producir un resultado. Es un tipo de Future que permite registrar devoluciones de llamada que se ejecutarán una vez que se realice el cálculo se complete o, si el procesamiento ya está completo, de inmediato.

ListenableFuture no forma parte del framework de Android y, en su lugar, se proporciona de Guava. Para obtener más información sobre el consulta la explicación de ListenableFuture.

Muchas bibliotecas de Jetpack existentes, como CameraX o los Servicios de salud tienen métodos asíncronos donde el tipo de datos que se muestra es un ListenableFuture que representa el estado de la ejecución. En algunos casos, es posible que debas implementar un método que devuelva un ListenableFuture, como para cumplir con los requisitos de TileService

Bibliotecas requeridas

Groovy

dependencies {
    implementation "com.google.guava:guava:31.0.1-android"

    // To use CallbackToFutureAdapter
    implementation "androidx.concurrent:concurrent-futures:1.2.0"

    // Kotlin
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0"
}

Kotlin

dependencies {
    implementation("com.google.guava:guava:31.0.1-android")

    // To use CallbackToFutureAdapter
    implementation("androidx.concurrent:concurrent-futures:1.2.0")

    // Kotlin
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0")
}

Cómo obtener el resultado de un ListenableFuture

Agrega una devolución de llamada

Usa la Futures.addCallback(...). método auxiliar para adjuntar devoluciones de llamada de éxito y fracaso a un ListenableFuture.

Kotlin

val future: ListenableFuture<QueryResult> = ...
Futures.addCallback(
    future,
    object : FutureCallback<QueryResult> {
        override fun onSuccess(result: QueryResult) {
            // handle success
        }

        override fun onFailure(t: Throwable) {
            // handle failure
        }
    },
    // causes the callbacks to be executed on the main (UI) thread
    context.mainExecutor
)

Java

ListenableFuture<QueryResult> future = ...
Futures.addCallback(
    future,
    new FutureCallback<QueryResult>() {
        public void onSuccess(QueryResult result) {
            // handle success
        }

        public void onFailure(@NonNull Throwable thrown) {
            // handle failure
        }
    },
    // causes the callbacks to be executed on the main (UI) thread
    context.getMainExecutor()
);

Suspensión en Kotlin

Cuando usas Kotlin, la forma más fácil de esperar el resultado de un ListenableFuture es usar await().

import kotlinx.coroutines.guava.await

...

val future: ListenableFuture<QueryResult> = ...
val queryResult = future.await() // suspends awaiting success

Interoperabilidad con RxJava

Un Single de RxJava se pueden crear a partir de un ListenableFuture registrando devoluciones de llamada dentro de un SingleEmitter

Kotlin

val future: ListenableFuture<QueryResult> = ...
val single = Single.create<QueryResult> {
    Futures.addCallback(future, object : FutureCallback<QueryResult> {
        override fun onSuccess(result: QueryResult) {
            it.onSuccess(result)
        }

        override fun onFailure(t: Throwable) {
            it.onError(t)
        }
    }, executor)
}

Java

ListenableFuture<QueryResult> future = ...
Single<QueryResult> single = Single.create(
        e -> Futures.addCallback(future, new FutureCallback<QueryResult>() {
            @Override
            public void onSuccess(QueryResult result) {
                e.onSuccess(result);
            }

            @Override
            public void onFailure(@NonNull Throwable thrown) {
                e.onError(thrown);
            }
        }, executor));

Cómo crear un ListenableFuture

Creación de un futuro inmediato

Si tu API no es asíncrona, pero necesitas unir el resultado de una solicitud en una ListenableFuture, puedes crear un ImmediateFuture. Esta se puede hacer con Futures.immediateFuture(...) método de fábrica.

Kotlin

fun getResult(): ListenableFuture<QueryResult> {
    try {
        val queryResult = getQueryResult()
        return Futures.immediateFuture(queryResult)
    } catch (e: Exception) {
        return Futures.immediateFailedFuture(e)
    }
}

Java

public ListenableFuture<QueryResult> getResult() {
    try {
        QueryResult queryResult = getQueryResult();
        return Futures.immediateFuture(queryResult);
    } catch (Exception e) {
        return Futures.immediateFailedFuture(e);
    }
}

Usa una corrutina

En Kotlin, un objeto future{ ... } se puede usar para convertir el resultado de una función de suspensión en un ListenableFuture.

import kotlinx.coroutines.guava.future

suspend fun getResultAsync(): QueryResult { ... }

fun getResultFuture(): ListenableFuture<QueryResult> {
    return coroutineScope.future{
        getResultAsync()
    }
}

Cómo convertir una devolución de llamada

Para convertir una API basada en devolución de llamada en una que use ListenableFuture, usa CallbackToFutureAdapter El artefacto androidx.concurrent:concurrent-futures proporciona esta API.

Consulta androidx.concurrent para obtener más información.

Realiza la conversión de Single de RxJava

Cuando usas RxJava, se genera un objeto Single. se puede convertir en SettableFuture que implementa ListenableFuture.

Kotlin

fun getResult(): ListenableFuture<QueryResult> {
    val single: Single<QueryResult> = ...

    val future = SettableFuture.create<QueryResult>()
    single.subscribe(future::set, future::setException)
    return future
}

Java

public ListenableFuture<QueryResult> getResult() {
    Single<QueryResult> single = ...

    SettableFuture<QueryResult> future = SettableFuture.create();
    single.subscribe(future::set, future::setException);
    return future;
}