Utiliser un ListenableFuture

Un ListenableFuture représente le résultat d'un calcul asynchrone, c'est-à-dire un calcul qui peut avoir fini de produire un résultat ou non. Il s'agit d'un type de Future qui vous permet d'enregistrer des rappels à exécuter une fois le calcul terminé, ou immédiatement, s'il est déjà terminé.

ListenableFuture ne fait pas partie du framework Android et est fourni par Guava. Pour en savoir plus sur l'implémentation de cette classe, consultez la section ListenableFuture en détail.

De nombreuses bibliothèques Jetpack existantes, telles que CameraX ou Services Santé, utilisent des méthodes asynchrones dans lesquelles le type renvoyé est un ListenableFuture représentant l'état de l'exécution. Dans certains cas, vous devrez peut-être implémenter une méthode qui renvoie un ListenableFuture, par exemple pour répondre aux exigences de TileService.

Bibliothèques requises

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

Obtenir le résultat d'une ListenableFuture

Ajouter un rappel

Utilisez la méthode d'aide Futures.addCallback(...) pour associer des rappels de réussite et d'échec à 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()
);

Suspension en Kotlin

Lorsque vous utilisez Kotlin, le moyen le plus simple d'attendre le résultat d'un ListenableFuture est d'utiliser await().

import kotlinx.coroutines.guava.await

...

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

Interopérabilité avec RxJava

Vous pouvez créer un Single RxJava à partir d'un ListenableFuture en enregistrant des rappels dans 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));

Créer un ListenableFuture

Créer un avenir immédiat

Si votre API n'est pas asynchrone, mais que vous devez encapsuler le résultat d'une opération terminée dans un ListenableFuture, vous pouvez créer une ImmediateFuture. Pour ce faire, utilisez la méthode de fabrique 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);
    }
}

Utiliser une coroutine

En langage Kotlin, un future{ ... } peut être utilisé pour convertir le résultat d'une fonction de suspension en ListenableFuture.

import kotlinx.coroutines.guava.future

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

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

Convertir un rappel

Pour convertir une API basée sur le rappel en une API qui utilise ListenableFuture, utilisez CallbackToFutureAdapter. Cette API est fournie par l'artefact androidx.concurrent:concurrent-futures.

Pour en savoir plus, consultez androidx.concurrent.

Conversion depuis RxJava Single

Lorsque vous utilisez RxJava, un Single peut être converti en SettableFuture, qui implémente 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;
}