שימוש ב-ListenableFuture

השדה ListenableFuture מייצג את התוצאה של חישוב אסינכרוני: חישוב שצפוי להפיק תוצאה או שלא הפיקו תוצאה עדיין. אלה סוג של Future שמאפשר לרשום קריאה חוזרת לביצוע או אם החישוב כבר הושלם, באופן מיידי.

האפליקציה ListenableFuture לא נכללת ב-framework של Android אלא מסופקת במקום זאת מאת Guava. לקבלת מידע נוסף על על ההטמעה של המחלקה הזו, ראו הסבר על 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.2.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.2.0")

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

קבלת התוצאה של ListenableFuture

הוספת קריאה חוזרת (callback)

שימוש בFutures.addCallback(...) עזר לשיוך קריאות חוזרות (callbacks) שהצליחו או נכשלו ל-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

RxJava Single אפשר ליצור אותו מ-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{ ... } אפשר להשתמש בו כדי להמיר את התוצאה של פונקציית השעיה ל-ListenableFuture.

import kotlinx.coroutines.guava.future

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

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

המרת קריאה חוזרת (callback)

כדי להמיר ממשק API מבוסס-קריאה חוזרת (callback) ל-API שמשתמש ב-ListenableFuture, צריך להשתמש ב- CallbackToFutureAdapter. ה-API הזה סופק על ידי פריט המידע שנוצר בתהליך הפיתוח (Artifact) מסוג 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;
}