Cómo usar un objeto ListenableFuture

Un ListenableFuture representa el resultado de un procesamiento asíncrono: un cálculo que puede haber terminado o no de producir un resultado. Es un tipo de Future que te permite registrar devoluciones de llamada para que se ejecuten una vez que se complete el procesamiento, o de inmediato si ya se completó.

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

Muchas bibliotecas de Jetpack existentes, como CameraX o Servicios de salud, tienen métodos asíncronos en los que el tipo de datos que se muestra es ListenableFuture, que representa el estado de la ejecución. En algunos casos, es posible que debas implementar un método que muestre un ListenableFuture, por ejemplo, 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.1.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.1.0")

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

Cómo obtener el resultado de un ListenableFuture

Cómo agregar una devolución de llamada

Usa el método auxiliar Futures.addCallback(...) 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

Se puede crear un Single RxJava 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 operación completada en un ListenableFuture, puedes crear un ImmediateFuture. Esto se puede hacer con el método de fábrica Futures.immediateFuture(...).

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

Cómo usar una corrutina

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

import kotlinx.coroutines.guava.future

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

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

Convierte 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 RxJava Single

Cuando se usa RxJava, un Single se puede convertir en un 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;
}