La API de Task es la forma estándar de controlar las operaciones asíncronas en los Servicios de Google Play. Proporciona una forma potente y flexible de administrar llamadas asíncronas, lo que reemplaza el patrón PendingResult anterior. Con Task, puedes encadenar varias llamadas, controlar flujos complejos y escribir controladores de éxito y falla claros.
Controla los resultados de las tareas
Muchas APIs de los Servicios de Google Play y Firebase muestran un objeto Task para representar operaciones asíncronas. Por ejemplo,
FirebaseAuth.signInAnonymously()
muestra un Task<AuthResult> que representa el resultado de la operación de acceso. El Task<AuthResult> indica que, cuando la tarea se complete
correctamente, mostrará un objeto AuthResult.
Para controlar el resultado de una Task, adjunta objetos de escucha que respondan a la finalización correcta, la falla o ambas:
Task<AuthResult> task = FirebaseAuth.getInstance().signInAnonymously();
Para controlar la finalización correcta de una tarea, adjunta un OnSuccessListener:
task.addOnSuccessListener(new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { // Task completed successfully // ... } });
Para controlar una tarea fallida, adjunta un OnFailureListener:
task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Para controlar el éxito y la falla en el mismo objeto de escucha, adjunta 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(); } } });
Administra subprocesos
De forma predeterminada, los objetos de escucha adjuntos a una Task se ejecutan en el subproceso principal (IU) de la aplicación. Esto significa que debes evitar realizar operaciones de larga duración en los objetos de escucha. Si necesitas realizar una operación de larga duración, puedes especificar un Executor que se use para programar objetos de escucha en un subproceso en segundo plano.
// 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) { // ... } });
Usa objetos de escucha con alcance de actividad
Cuando necesites controlar los resultados de las tareas dentro de una Activity, es importante administrar el ciclo de vida de los objetos de escucha para evitar que se llamen cuando la Activity ya no esté visible. Para ello, puedes usar objetos de escucha con alcance de actividad. Estos objetos de escucha se quitan automáticamente cuando se llama al método onStop de tu Activity, de modo que no se ejecuten después de que se detenga la Activity.
Activity activity = MainActivity.this; task.addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { // ... } });
Encadena tareas
Si usas un conjunto de APIs que muestran objetos Task en una función compleja, puedes encadenarlos con continuaciones. Esto te ayuda a evitar devoluciones de llamada anidadas y consolida el control de errores para varias tareas encadenadas.
Por ejemplo, considera una situación en la que tienes un método doSomething que
muestra un Task<String>, pero requiere un AuthResult como parámetro.
Puedes obtener este AuthResult de forma asíncrona desde otra Task:
public Task<String> doSomething(AuthResult authResult) {
// ...
}Con el método Task.continueWithTask, puedes encadenar estas dos tareas:
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. // ... } });
Bloquea una tarea
Si tu programa ya se está ejecutando en un subproceso en segundo plano, puedes bloquear el subproceso actual y esperar a que se complete la tarea, en lugar de usar una devolución de llamada:
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.
// ...
}También puedes especificar un tiempo de espera cuando bloqueas una tarea para evitar que tu aplicación se bloquee de forma indefinida si la tarea tarda demasiado en completarse:
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.
// ...
}Interoperabilidad
Task está diseñado para funcionar bien con otros patrones comunes de programación asíncrona de Android. Se puede convertir a y desde otras primitivas, como
ListenableFuture y corrutinas de Kotlin, que
recomienda AndroidX,
lo que te permite usar el enfoque que mejor se adapte a tus necesidades.
Este es un ejemplo en el que se usa una Task:
// ... simpleTask.addOnCompleteListener(this) { completedTask -> textView.text = completedTask.result }
Corrutiina de Kotlin
Para usar corrutinas de Kotlin con Task, agrega la siguiente dependencia a tu proyecto y, luego, usa el fragmento de código para convertir desde una Task.
Gradle (nivel del módulo build.gradle, por lo general, 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'
Fragmento
import kotlinx.coroutines.tasks.await // ... textView.text = simpleTask.await() }
ListenableFuture de Guava
Para usar ListenableFuture de Guava con Task, agrega la siguiente dependencia a tu proyecto y, luego, usa el fragmento de código para convertir desde una Task.
Gradle (nivel del módulo build.gradle, por lo general, app/build.gradle)
implementation "androidx.concurrent:concurrent-futures:1.2.0"
Fragmento
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
Agrega la siguiente dependencia, además de la biblioteca asíncrona relativa que elijas, a tu proyecto y, luego, usa el fragmento de código para convertir desde una Task.
Gradle (nivel del módulo build.gradle, por lo general, app/build.gradle)
// Source: https://github.com/ashdavies/rx-tasks implementation 'io.ashdavies.rx.rxtasks:rx-tasks:2.2.0'
Fragmento
import io.ashdavies.rx.rxtasks.toSingle import java.util.concurrent.TimeUnit // ... simpleTask.toSingle(this).subscribe { result -> textView.text = result }
Próximos pasos
Taskreferencia- Referencia de
Tasks