API Tasks

À partir de la version 9.0.0 des services Google Play, vous pouvez utiliser une API Task et un certain nombre de méthodes qui renvoient Task ou ses sous-classes. Task est une API qui représente les appels de méthode asynchrones, semblable à PendingResult dans les versions précédentes des services Google Play.

Gérer les résultats des tâches

FirebaseAuth.signInAnonymously() est une méthode courante qui renvoie un Task. Elle renvoie Task<AuthResult>, ce qui signifie que la tâche renvoie un objet AuthResult en cas de réussite:

Task<AuthResult> task = FirebaseAuth.getInstance().signInAnonymously();

Pour recevoir une notification lorsque la tâche aboutit, joignez OnSuccessListener:

task.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
    @Override
    public void onSuccess(AuthResult authResult) {
        // Task completed successfully
        // ...
    }
});

Pour être averti en cas d'échec de la tâche, joignez OnFailureListener:

task.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // Task failed with an exception
        // ...
    }
});

Pour gérer la réussite et l'échec dans le même écouteur, associez un OnCompleteListener:

task.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        if (task.isSuccessful()) {
            // Task completed successfully
            AuthResult result = task.getResult();
        } else {
            // Task failed with an exception
            Exception exception = task.getException();
        }
    }
});

Exécution de threads

Les écouteurs associés à un thread sont exécutés par défaut sur le thread principal de l'application (UI). Lorsque vous associez un écouteur, vous pouvez également spécifier un Executor utilisé pour planifier des écouteurs.

// Create a new ThreadPoolExecutor with 2 threads for each processor on the
// device and a 60 second keep-alive time.
int numCores = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(numCores * 2, numCores *2,
        60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

task.addOnCompleteListener(executor, new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        // ...
    }
});

Écouteurs de portée activité

Si vous écoutez les résultats d'une tâche dans une Activity, vous pouvez ajouter des écouteurs de portée activité à la tâche. Ces écouteurs sont supprimés lors de la méthode onStop de votre activité, afin qu'ils ne soient pas appelés lorsque l'activité n'est plus visible.

Activity activity = MainActivity.this;
task.addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        // ...
    }
});

Chaîne

Si vous utilisez plusieurs API qui renvoient Task, vous pouvez les enchaîner à l'aide d'une continuation. Cela permet d'éviter les rappels profondément imbriqués et de consolider la gestion des erreurs pour les chaînes de tâches.

Par exemple, la méthode doSomething renvoie un objet Task<String>, mais nécessite un élément AuthResult, que nous obtiendrons de manière asynchrone à partir d'une tâche:

public Task<String> doSomething(AuthResult authResult) {
    // ...
}

La méthode Task.continueWithTask nous permet d'enchaîner ces deux tâches:

Task<AuthResult> signInTask = FirebaseAuth.getInstance().signInAnonymously();

signInTask.continueWithTask(new Continuation<AuthResult, Task<String>>() {
    @Override
    public Task<String> then(@NonNull Task<AuthResult> task) throws Exception {
        // Take the result from the first task and start the second one
        AuthResult result = task.getResult();
        return doSomething(result);
    }
}).addOnSuccessListener(new OnSuccessListener<String>() {
    @Override
    public void onSuccess(String s) {
        // Chain of tasks completed successfully, got result from last task.
        // ...
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // One of the tasks in the chain failed with an exception.
        // ...
    }
});

Vidéo bloquée

Si votre programme s'exécute déjà dans un thread en arrière-plan, vous pouvez bloquer une tâche pour obtenir le résultat de manière synchrone et éviter les rappels:

try {
    // Block on a task and get the result synchronously. This is generally done
    // when executing a task inside a separately managed background thread. Doing this
    // on the main (UI) thread can cause your application to become unresponsive.
    AuthResult authResult = Tasks.await(task);
} catch (ExecutionException e) {
    // The Task failed, this is the same exception you'd get in a non-blocking
    // failure handler.
    // ...
} catch (InterruptedException e) {
    // An interrupt occurred while waiting for the task to complete.
    // ...
}

Vous pouvez également spécifier un délai avant expiration lors du blocage d'une tâche afin que votre application ne se bloque pas:

try {
    // Block on the task for a maximum of 500 milliseconds, otherwise time out.
    AuthResult authResult = Tasks.await(task, 500, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
    // ...
} catch (InterruptedException e) {
    // ...
} catch (TimeoutException e) {
    // Task timed out before it could complete.
    // ...
}

Interopérabilité

Un Task s'aligne conceptuellement sur plusieurs approches Android courantes de gestion du code asynchrone, et un Task peut être facilement converti en d'autres primitives, y compris les coroutines ListenableFuture et Kotlin, recommandées par AndroidX.

Voici un exemple utilisant un Task:

// ...
simpleTask.addOnCompleteListener(this) {
  completedTask -> textView.text = completedTask.result
}

Coroutine Kotlin

Utilisation

Ajoutez la dépendance suivante à votre projet et utilisez le code ci-dessous pour effectuer la conversion à partir d'un Task.

Gradle (build.gradle au niveau du module, généralement app/build.gradle)
// Source: https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.1'
Snippet
import kotlinx.coroutines.tasks.await
// ...
  textView.text = simpleTask.await()
}

Guava ListenableFuture

Ajoutez la dépendance suivante à votre projet et utilisez le code ci-dessous pour effectuer la conversion à partir d'un Task.

Gradle (build.gradle au niveau du module, généralement app/build.gradle)
implementation "androidx.concurrent:concurrent-futures:1.1.0"
Snippet
import com.google.common.util.concurrent.ListenableFuture
// ...
/** Convert Task to ListenableFuture. */
fun <T> taskToListenableFuture(task: Task<T>): ListenableFuture<T> {
  return CallbackToFutureAdapter.getFuture { completer ->
    task.addOnCompleteListener { completedTask ->
      if (completedTask.isCanceled) {
        completer.setCancelled()
      } else if (completedTask.isSuccessful) {
        completer.set(completedTask.result)
      } else {
        val e = completedTask.exception
        if (e != null) {
          completer.setException(e)
        } else {
          throw IllegalStateException()
        }
      }
    }
  }
}
// ...
this.listenableFuture = taskToListenableFuture(simpleTask)
this.listenableFuture?.addListener(
  Runnable {
    textView.text = listenableFuture?.get()
  },
  ContextCompat.getMainExecutor(this)
)

RxJava2 Observable

Ajoutez la dépendance suivante à votre projet, en plus de la bibliothèque asynchrone relative de votre choix, et utilisez le code ci-dessous pour effectuer la conversion à partir d'un Task.

Gradle (build.gradle au niveau du module, généralement app/build.gradle)
// Source: https://github.com/ashdavies/rx-tasks
implementation 'io.ashdavies.rx.rxtasks:rx-tasks:2.2.0'
Snippet
import io.ashdavies.rx.rxtasks.toSingle
import java.util.concurrent.TimeUnit
// ...
simpleTask.toSingle(this).subscribe { result -> textView.text = result }

Étapes suivantes