Task
API — это стандартный способ обработки асинхронных операций в сервисах Google Play. Он обеспечивает мощный и гибкий способ управления асинхронными вызовами, заменяя старый шаблон PendingResult
. С помощью Task
можно объединять несколько вызовов в цепочку, обрабатывать сложные потоки и писать четкие обработчики успехов и неудач.
Обработка результатов задач
Многие API в сервисах Google Play и Firebase возвращают объект Task
для представления асинхронных операций. Например, FirebaseAuth.signInAnonymously()
возвращает Task<AuthResult>
, который представляет результат операции входа. Task<AuthResult>
указывает, что при успешном завершении задачи будет возвращен объект AuthResult
.
Вы можете обработать результат Task
, присоединив прослушиватели, которые реагируют на успешное завершение, неудачу или и то, и другое:
Task<AuthResult> task = FirebaseAuth.getInstance().signInAnonymously();
Для обработки успешного завершения задачи присоедините OnSuccessListener
:
task.addOnSuccessListener(new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { // Task completed successfully // ... } });
Для обработки невыполненной задачи присоедините OnFailureListener
:
task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Чтобы обработать как успешные, так и неудачные события в одном прослушивателе, присоедините 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(); } } });
Управлять потоками
По умолчанию прослушиватели, прикрепленные к Task
запускаются в основном потоке приложения (UI). Это означает, что следует избегать выполнения длительных операций в прослушивателях. Если вам нужно выполнить длительную операцию, вы можете указать Executor
, который используется для планирования прослушивателей в фоновом потоке.
// 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) { // ... } });
Используйте прослушиватели, ограниченные активностью
Когда вам нужно обрабатывать результаты задач в Activity
, важно управлять жизненным циклом слушателей, чтобы предотвратить их вызов, когда Activity
больше не видно. Для этого вы можете использовать слушателей, ограниченных активностью. Эти слушатели автоматически удаляются при вызове метода onStop
вашей Activity
, так что они не будут выполняться после остановки Activity
.
Activity activity = MainActivity.this; task.addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { // ... } });
Цепочка задач
Если вы используете набор API, которые возвращают объекты Task
в сложной функции, вы можете связать их вместе с помощью продолжений. Это поможет вам избежать глубоко вложенных обратных вызовов и консолидирует обработку ошибок для нескольких связанных задач.
Например, рассмотрим сценарий, в котором у вас есть метод doSomething
, который возвращает Task<String>
, но он требует AuthResult
в качестве параметра. Вы можете получить этот AuthResult
асинхронно из другой Task
:
public Task<String> doSomething(AuthResult authResult) { // ... }
Используя метод Task.continueWithTask
, вы можете объединить эти две задачи:
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. // ... } });
Заблокировать задачу
Если ваша программа уже выполняется в фоновом потоке, вы можете заблокировать текущий поток и дождаться завершения задачи вместо использования обратного вызова:
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. // ... }
Вы также можете указать тайм-аут при блокировке задачи, чтобы предотвратить зависание приложения на неопределенный срок, если выполнение задачи занимает слишком много времени:
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. // ... }
Взаимодействие
Task
разработан для хорошей работы с другими распространенными шаблонами асинхронного программирования Android. Его можно преобразовывать в и из других примитивов, таких как ListenableFuture
и сопрограммы Kotlin, которые рекомендуются AndroidX , что позволяет использовать подход, который лучше всего соответствует вашим потребностям.
Вот пример использования Task
:
// ... simpleTask.addOnCompleteListener(this) { completedTask -> textView.text = completedTask.result }
сопрограмма Kotlin
Чтобы использовать сопрограммы Kotlin с Task
, добавьте в свой проект следующую зависимость, а затем используйте фрагмент кода для преобразования из Task
.
Gradle ( build.gradle
на уровне модуля, обычно 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'
Фрагмент
import kotlinx.coroutines.tasks.await // ... textView.text = simpleTask.await() }
Гуава ListenableFuture
Чтобы использовать Guava ListenableFuture
с Task
, добавьте следующую зависимость в свой проект, а затем используйте фрагмент кода для преобразования из Task
.
Gradle ( build.gradle
на уровне модуля, обычно app/build.gradle
)
implementation "androidx.concurrent:concurrent-futures:1.2.0"
Фрагмент
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
Добавьте в свой проект следующую зависимость в дополнение к соответствующей асинхронной библиотеке по вашему выбору, а затем используйте фрагмент кода для преобразования из Task
.
Gradle ( build.gradle
на уровне модуля, обычно app/build.gradle
)
// Source: https://github.com/ashdavies/rx-tasks implementation 'io.ashdavies.rx.rxtasks:rx-tasks:2.2.0'
Фрагмент
import io.ashdavies.rx.rxtasks.toSingle import java.util.concurrent.TimeUnit // ... simpleTask.toSingle(this).subscribe { result -> textView.text = result }