Como usar um ListenableFuture

Um ListenableFuture representa o resultado de um cálculo assíncrono: um computação que pode ou não ter concluído a produção de um resultado. É um tipo de Future que permite registrar callbacks a serem executados quando o cálculo for concluído, ou se o cálculo já estiver concluído, imediatamente.

ListenableFuture não faz parte do framework do Android e é fornecido por Guava. Para mais informações sobre o implementação dessa classe, consulte a explicação do ListenableFuture.

Muitas bibliotecas do Jetpack, como CameraX ou Recursos de saúde têm métodos assíncronos em que o tipo de retorno é ListenableFuture, que representa o status da a execução. Em alguns casos, pode ser necessário implementar um método que retorne uma ListenableFuture, para atender aos requisitos de TileService.

Bibliotecas necessárias

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

Como receber o resultado de um ListenableFuture

Como adicionar uma chamada de retorno

Usar o Futures.addCallback(...) método auxiliar para anexar callbacks de sucesso e falha a um 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()
);

Suspensão no Kotlin

Ao usar o Kotlin, a maneira mais fácil de esperar pelo resultado de um ListenableFuture é usar await().

import kotlinx.coroutines.guava.await

...

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

Interoperabilidade com RxJava

Um Single RxJava podem ser criadas a partir de ListenableFuture, registrando callbacks dentro de 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));

Como criar um ListenableFuture

Criação de um futuro imediato

Se a API não for assíncrona, mas você precisar unir o resultado de um operação em um ListenableFuture, é possível criar um ImmediateFuture. Isso pode ser feito usando o Futures.immediateFuture(...) 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);
    }
}

Como usar uma corrotina

Em Kotlin, um future{ ... } (link em inglês) pode ser usada para converter o resultado de uma função de suspensão em um ListenableFuture.

import kotlinx.coroutines.guava.future

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

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

Como converter um callback

Para converter uma API baseada em callback em uma que usa ListenableFuture, use CallbackToFutureAdapter. Essa API é fornecida pelo artefato androidx.concurrent:concurrent-futures.

Consulte androidx.concurrent para mais informações.

Como converter do Single do RxJava

Ao usar RxJava, um Single podem ser convertidos em um 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;
}