Usare un futuro ascoltabile

Un ListenableFuture rappresenta il risultato di un calcolo asincrono: un calcolo che potrebbe aver terminato o meno di produrre un risultato. È un tipo di Future che consente di registrare i callback da eseguire subito dopo il completamento del calcolo o, se il calcolo è già stato completato.

ListenableFuture non fa parte del framework Android ed è fornito da Guava. Per ulteriori informazioni sull'implementazione di questo corso, consulta la spiegazione diListenableFuture.

Molte librerie Jetpack esistenti, come FotocameraX o Health Services, dispongono di metodi asincroni in cui il tipo restituito è ListenableFuture, che rappresenta lo stato dell'esecuzione. In alcuni casi, potrebbe essere necessario implementare un metodo che restituisca un elemento ListenableFuture, ad esempio per soddisfare i requisiti di TileService.

Librerie richieste

Trendy

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

Recupero del risultato di ListenableFuture in corso...

Aggiunta di un callback

Utilizza il metodo helper Futures.addCallback(...) per collegare i callback di operazione riuscita e non riuscita 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()
);

Sospensione in Kotlin

Quando utilizzi Kotlin, il modo più semplice di attendere il risultato di AscoltaableFuture è utilizzare await().

import kotlinx.coroutines.guava.await

...

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

Interopera con RxJava

È possibile creare un codice RxJava Single da un elemento ListenableFuture registrando i callback all'interno di un elemento 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));

Creazione di un ListenableFuture in corso...

Creare un futuro immediato

Se l'API non è asincrona, ma devi eseguire il wrapping del risultato di un'operazione completata in un ListenableFuture, puoi creare un elemento ImmediateFuture. Questa operazione può essere eseguita utilizzando il metodo di fabbrica 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);
    }
}

Utilizzo di una coroutine

In Kotlin, è possibile utilizzare un elemento future{ ... } per convertire il risultato di una funzione di sospensione in un ListenableFuture.

import kotlinx.coroutines.guava.future

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

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

Conversione di un callback

Per convertire un'API basata su callback in un'API che utilizza ListenableFuture, utilizza CallbackToFutureAdapter. Questa API è fornita dall'artefatto androidx.concurrent:concurrent-futures.

Per ulteriori informazioni, visita la pagina androidx.concurrent.

Conversione da RxJava Single in corso...

Quando utilizzi RxJava, una Single può essere convertita in un SettableFuture, che 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;
}