A API Tasks

A partir da versão 9.0.0 do Google Play Services, é possível usar uma API Task e uma Número de métodos que retornam Task ou as subclasses dela. Task é uma API que representa chamadas de método assíncronas, semelhante a PendingResult nas do Google Play Services.

Como processar resultados de tarefas

Um método comum que retorna um Task é FirebaseAuth.signInAnonymously(). Ela retorna um Task<AuthResult>, o que significa que a tarefa retornará um objeto AuthResult quando ele é bem-sucedido:

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

Para receber uma notificação quando a tarefa for concluída, anexe um OnSuccessListener:

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

Para receber uma notificação quando a tarefa falhar, anexe um OnFailureListener:

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

Para lidar com sucessos e falhas no mesmo listener, anexe um 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();
        }
    }
});

Linha de execução

Os listeners anexados a uma linha de execução são executados na linha de execução principal (interface) do aplicativo por padrão. Ao anexar um listener, você também pode especificar um Executor que seja usada para programar listeners.

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

Listeners com escopo de atividade

Se você estiver ouvindo resultados de tarefas em uma Activity, recomendamos adicionar listeners com escopo de atividade à tarefa. Esses listeners são removidos durante o método onStop da sua atividade para que os listeners não sejam chamados; quando a atividade não estiver mais visível.

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

Encadeamento

Se você usa várias APIs que retornam Task, é possível encadeá-las usando uma continuação. Isso ajuda a evitar callbacks profundamente aninhados e consolida tratamento de erros para cadeias de tarefas.

Por exemplo, o método doSomething retorna um Task<String>, mas exige um AuthResult, que vamos receber de forma assíncrona de uma tarefa:

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

Usando o método Task.continueWithTask, podemos encadear estas duas tarefas:

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

Bloqueio

Caso seu programa já esteja sendo executado em uma linha de execução em segundo plano, é possível bloquear uma tarefa para obter o resultado de forma síncrona e evitar callbacks:

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

Também é possível especificar um tempo limite ao bloquear uma tarefa para que seu aplicativo não trava:

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

Interoperabilidade

Um Task se alinha conceitualmente a várias abordagens conhecidas do Android para gerenciar código assíncrono, e um Task pode ser convertido diretamente em outro primitivos, incluindo as corrotinas ListenableFuture e Kotlin, que são recomendado pelo AndroidX.

Confira um exemplo usando um Task:

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

Corrotina do Kotlin

Uso

Adicione a dependência abaixo ao projeto e use o código abaixo para converter de um Task.

Gradle (nível do módulo build.gradle, geralmente 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

Adicione a dependência abaixo ao projeto e use o código abaixo para converter de um Task.

Gradle (nível do módulo build.gradle, geralmente 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 observável

Adicione a dependência a seguir, além da biblioteca assíncrona relativa de opção, para seu projeto e use o código abaixo para converter de um Task.

Gradle (nível do módulo build.gradle, geralmente 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 }

Próximas etapas