Sử dụng ListenableFuture

ListenableFuture thể hiện kết quả của một phép tính không đồng bộ: một phép tính có thể đã hoặc chưa hoàn tất việc tạo ra kết quả. Đây là một loại Future cho phép bạn đăng ký các lệnh gọi lại để thực thi sau khi quá trình tính toán hoàn tất, hoặc nếu quá trình tính toán đã hoàn tất ngay lập tức.

ListenableFuture không phải là một phần của khung Android mà thay vào đó do Guava cung cấp. Để biết thêm thông tin về cách triển khai lớp này, hãy xem phần Giải thích về ListenableFuture.

Nhiều thư viện Jetpack hiện có như CameraX hoặc Dịch vụ sức khoẻ có các phương thức không đồng bộ, trong đó loại dữ liệu trả về là ListenableFuture biểu thị trạng thái thực thi. Trong một số trường hợp, bạn có thể cần triển khai phương thức trả về ListenableFuture, chẳng hạn như để đáp ứng các yêu cầu của TileService.

Thư viện bắt buộc

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

Nhận kết quả của ListenableFuture

Thêm lệnh gọi lại

Sử dụng phương thức trình trợ giúp Futures.addCallback(...) để đính kèm các lệnh gọi lại thành công và không thành công vào 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()
);

Tạm ngưng trong Kotlin

Khi sử dụng Kotlin, cách dễ nhất để đợi kết quả của ListenableFuture là sử dụng await().

import kotlinx.coroutines.guava.await

...

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

Tương tác với RxJava

Bạn có thể tạo một RxJava Single từ ListenableFuture bằng cách đăng ký các lệnh gọi lại bên trong một 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));

Tạo một ListenableFuture

Xây dựng tương lai tức thì

Nếu API của bạn không đồng bộ nhưng bạn cần gói kết quả của một thao tác đã hoàn tất vào một ListenableFuture, thì bạn có thể tạo ImmediateFuture. Bạn có thể thực hiện việc này bằng cách sử dụng phương thức gốc 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);
    }
}

Sử dụng coroutine

Trong Kotlin, bạn có thể dùng future{ ... } để chuyển đổi kết quả của hàm tạm ngưng thành ListenableFuture.

import kotlinx.coroutines.guava.future

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

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

Chuyển đổi một lệnh gọi lại

Để chuyển đổi API dựa trên lệnh gọi lại thành API sử dụng ListenableFuture, hãy sử dụng CallbackToFutureAdapter. API này do cấu phần phần mềm androidx.concurrent:concurrent-futures cung cấp.

Xem androidx.concurrent để biết thêm thông tin.

Chuyển đổi từ RxJava Single

Khi sử dụng RxJava, Single có thể được chuyển đổi thành SettableFuture, giúp triển khai 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;
}