Использование ListenableFuture

ListenableFuture представляет собой результат асинхронного вычисления, которое может быть завершено или нет. Это тип Future, который позволяет регистрировать обратные вызовы, выполняемые после завершения вычислений или сразу, если вычисления уже завершены.

ListenableFuture не входит в платформу Android, а предоставляется Guava. Подробную информацию о реализации этого класса можно найти в статье ListenableFuture explained (Общие сведения о ListenableFuture).

Многие существующие библиотеки Jetpack, такие как CameraX или сервисы наблюдения за здоровьем, имеют асинхронные методы, в которых тип возвращаемого значения – ListenableFuture, представляющий статус выполнения. В некоторых случаях может потребоваться реализовать метод, который возвращает ListenableFuture, например чтобы выполнить требования TileService.

Необходимые библиотеки

Классный

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

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

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

Котлин

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

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

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

Получение результата ListenableFuture

Добавление обратного вызова

Используйте вспомогательный метод Futures.addCallback(...), чтобы прикрепить обратные вызовы успешного и неудачного выполнения к 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()
);

Приостановка в Kotlin

Если вы используете Kotlin, проще всего дождаться результата ListenableFuture с помощью await().

import kotlinx.coroutines.guava.await

...

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

Совместимость с RxJava

Объект Single RxJava можно создать из ListenableFuture, зарегистрировав обратные вызовы в 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));

Создание ListenableFuture

Создание ближайшего будущего

Если ваш API не асинхронный, но вам нужно упаковать результат завершенной операции в ListenableFuture, вы можете создать ImmediateFuture. Для этого можно использовать фабричный метод 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);
    }
}

Использование сопрограммы

В Kotlin можно использовать future{ ... }, чтобы преобразовать результат функции suspend в ListenableFuture.

import kotlinx.coroutines.guava.future

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

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

Преобразование обратного вызова

Чтобы преобразовать API на основе обратного вызова в API, использующий ListenableFuture, используйте CallbackToFutureAdapter. Этот API предоставляется артефактом androidx.concurrent:concurrent-futures.

Подробную информацию можно найти на странице androidx.concurrent.

Переход с RxJava Single

При использовании RxJava объект Single можно преобразовать в SettableFuture, который реализует 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;
}