<br />

The `Task` API is the standard way to handle asynchronous operations in Google
Play services. It provides a powerful and flexible way to manage asynchronous
calls, replacing the older `PendingResult` pattern. With `Task`, you can chain
multiple calls, handle complex flows, and write clear success and failure
handlers.

## Handle task results

Many APIs in Google Play services and Firebase return a `Task` object to
represent asynchronous operations. For example,
[`FirebaseAuth.signInAnonymously()`](https://developers.google.com/android/reference/com/google/firebase/auth/FirebaseAuth)
returns a `Task<AuthResult>` which represents the result of the sign-in
operation. The `Task<AuthResult>` indicates that when the task completes
successfully, it will return an `AuthResult` object.

You can handle the result of a `Task` by attaching listeners that respond to
successful completion, failure, or both:

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

To handle a successful task completion, attach an `OnSuccessListener`:

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

To handle a failed task, attach an `OnFailureListener`:

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

To handle both success and failure in the same listener, attach an
`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();
        }
    }
});
```

## Manage threads

By default, listeners attached to a `Task` are run on the application main (UI)
thread. This means that you should avoid doing long-running operations in
listeners. If you need to perform a long-running operation, you can specify an
`Executor` that is used to schedule listeners on a background thread.

```
// 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) {
        // ...
    }
});
```

## Use activity-scoped listeners

When you need to handle task results within an `Activity`, it's important to
manage the listeners' lifecycle to prevent them from being called when the
`Activity` is no longer visible. To do this, you can use activity-scoped
listeners. These listeners are automatically removed when the `onStop` method of
your `Activity` is called, so that they won't be executed after the
`Activity` is stopped.

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

## Chain tasks

If you are using a set of APIs that return `Task` objects in a complex function,
you can chain them together using continuations. This helps you avoid deeply
nested callbacks and consolidates error handling for multiple chained tasks.

For example, consider a scenario where you have a method `doSomething` that
returns a `Task<String>`, but it requires an `AuthResult` as a parameter.
You can obtain this `AuthResult` asynchronously from another `Task`:

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

Using the `Task.continueWithTask` method, you can chain these two tasks:

```
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.
        // ...
    }
});
```

## Block a task

If your program is already executing in a background thread, you can block the
current thread and wait for the task to complete, instead of using a callback:

```
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.
    // ...
}
```

You can also specify a timeout when blocking a task to prevent your application
from getting stuck indefinitely if the task takes too long to complete:

```
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.
    // ...
}
```

## Interoperability

`Task` is designed to work well with other common Android asynchronous
programming patterns. It can be converted to and from other primitives like
`ListenableFuture` and Kotlin coroutines, which are
[recommended by AndroidX](https://developer.android.com/jetpack/androidx/releases/concurrent),
allowing you to use the approach that best fits your needs.

Here's an example using a `Task`:

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

### Kotlin coroutine

To use Kotlin coroutines with `Task`, add the following dependency to your
project and then use the code snippet to convert from a `Task`.

##### Gradle (module-level `build.gradle`, usually `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.7.3'
```

##### Snippet

```
import kotlinx.coroutines.tasks.await
// ...
  textView.text = simpleTask.await()
}
```

### Guava `ListenableFuture`

To use Guava `ListenableFuture` with `Task`, add the following dependency to
your project and then use the code snippet to convert from a `Task`.

##### Gradle (module-level `build.gradle`, usually `app/build.gradle`)

```
implementation "androidx.concurrent:concurrent-futures:1.2.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`

Add the following dependency, in addition to the relative async library of
choice, to your project and then use the code snippet to convert from a `Task`.

##### Gradle (module-level `build.gradle`, usually `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 }
```

## Next steps

- [`Task` reference](https://developers.google.com/android/reference/com/google/android/gms/tasks/Task.md.txt)
- [`Tasks` reference](https://developers.google.com/android/reference/com/google/android/gms/tasks/Tasks.md.txt)