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

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

ListenableFuture не является частью платформы Android и вместо этого предоставляется Guava . Дополнительные сведения о реализации этого класса см. в разделе «Объяснение ListenableFuture» .

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

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

классный

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

Котлин

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

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

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

Используйте вспомогательный метод Futures.addCallback(...) для прикрепления обратных вызовов успешного и неудачного выполнения к ListenableFuture .

Котлин

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
)

Ява

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 самый простой способ дождаться результата ListenableFuture — использовать await() .

import kotlinx.coroutines.guava.await

...

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

Взаимодействие с RxJava

RxJava Single можно создать из ListenableFuture путем регистрации обратных вызовов внутри SingleEmitter .

Котлин

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

Ява

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(...) .

Котлин

fun getResult(): ListenableFuture<QueryResult> {
    try {
        val queryResult = getQueryResult()
        return Futures.immediateFuture(queryResult)
    } catch (e: Exception) {
        return Futures.immediateFailedFuture(e)
    }
}

Ява

public ListenableFuture<QueryResult> getResult() {
    try {
        QueryResult queryResult = getQueryResult();
        return Futures.immediateFuture(queryResult);
    } catch (Exception e) {
        return Futures.immediateFailedFuture(e);
    }
}

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

В Kotlin future{ ... } можно использовать для преобразования результата функции приостановки в 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 .

Котлин

fun getResult(): ListenableFuture<QueryResult> {
    val single: Single<QueryResult> = ...

    val future = SettableFuture.create<QueryResult>()
    single.subscribe(future::set, future::setException)
    return future
}

Ява

public ListenableFuture<QueryResult> getResult() {
    Single<QueryResult> single = ...

    SettableFuture<QueryResult> future = SettableFuture.create();
    single.subscribe(future::set, future::setException);
    return future;
}