Back to project page Bolts-Android.
The source code is released under:
BSD License For Bolts software Copyright (c) 2013, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that t...
If you think the Android project Bolts-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright (c) 2014, Facebook, Inc.// w ww . j a v a 2s . c o m * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ package bolts; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * Represents the result of an asynchronous operation. * * @param <TResult> * The type of the result of the task. */ public class Task<TResult> { /** * An {@link java.util.concurrent.Executor} that executes tasks in parallel. */ public static final ExecutorService BACKGROUND_EXECUTOR = BoltsExecutors.background(); /** * An {@link java.util.concurrent.Executor} that executes tasks in the current thread unless * the stack runs too deep, at which point it will delegate to {@link Task#BACKGROUND_EXECUTOR} in * order to trim the stack. */ private static final Executor IMMEDIATE_EXECUTOR = BoltsExecutors.immediate(); /** * An {@link java.util.concurrent.Executor} that executes tasks on the UI thread. */ public static final Executor UI_THREAD_EXECUTOR = AndroidExecutors.uiThread(); private final Object lock = new Object(); private boolean complete; private boolean cancelled; private TResult result; private Exception error; private List<Continuation<TResult, Void>> continuations; private Task() { continuations = new ArrayList<Continuation<TResult, Void>>(); } /** * Creates a TaskCompletionSource that orchestrates a Task. This allows the creator of a task to * be solely responsible for its completion. * * @return A new TaskCompletionSource. */ public static <TResult> Task<TResult>.TaskCompletionSource create() { Task<TResult> task = new Task<TResult>(); return task.new TaskCompletionSource(); } /** * @return {@code true} if the task completed (has a result, an error, or was cancelled. * {@code false} otherwise. */ public boolean isCompleted() { synchronized (lock) { return complete; } } /** * @return {@code true} if the task was cancelled, {@code false} otherwise. */ public boolean isCancelled() { synchronized (lock) { return cancelled; } } /** * @return {@code true} if the task has an error, {@code false} otherwise. */ public boolean isFaulted() { synchronized (lock) { return error != null; } } /** * @return The result of the task, if set. {@code null} otherwise. */ public TResult getResult() { synchronized (lock) { return result; } } /** * @return The error for the task, if set. {@code null} otherwise. */ public Exception getError() { synchronized (lock) { return error; } } /** * Blocks until the task is complete. */ public void waitForCompletion() throws InterruptedException { synchronized (lock) { if (!isCompleted()) { lock.wait(); } } } /** * Creates a completed task with the given value. */ public static <TResult> Task<TResult> forResult(TResult value) { Task<TResult>.TaskCompletionSource tcs = Task.create(); tcs.setResult(value); return tcs.getTask(); } /** * Creates a faulted task with the given error. */ public static <TResult> Task<TResult> forError(Exception error) { Task<TResult>.TaskCompletionSource tcs = Task.create(); tcs.setError(error); return tcs.getTask(); } /** * Creates a cancelled task. */ public static <TResult> Task<TResult> cancelled() { Task<TResult>.TaskCompletionSource tcs = Task.create(); tcs.setCancelled(); return tcs.getTask(); } /** * Makes a fluent cast of a Task's result possible, avoiding an extra continuation just to cast * the type of the result. */ public <TOut> Task<TOut> cast() { @SuppressWarnings("unchecked") Task<TOut> task = (Task<TOut>) this; return task; } /** * Turns a Task<T> into a Task<Void>, dropping any result. */ public Task<Void> makeVoid() { return this.continueWithTask(new Continuation<TResult, Task<Void>>() { @Override public Task<Void> then(Task<TResult> task) throws Exception { if (task.isCancelled()) { return Task.cancelled(); } if (task.isFaulted()) { return Task.forError(task.getError()); } return Task.forResult(null); } }); } /** * Invokes the callable on a background thread, returning a Task to represent the operation. */ public static <TResult> Task<TResult> callInBackground(Callable<TResult> callable) { return call(callable, BACKGROUND_EXECUTOR); } /** * Invokes the callable using the given executor, returning a Task to represent the operation. */ public static <TResult> Task<TResult> call(final Callable<TResult> callable, Executor executor) { final Task<TResult>.TaskCompletionSource tcs = Task.create(); executor.execute(new Runnable() { @Override public void run() { try { tcs.setResult(callable.call()); } catch (Exception e) { tcs.setError(e); } } }); return tcs.getTask(); } /** * Invokes the callable on the current thread, producing a Task. */ public static <TResult> Task<TResult> call(final Callable<TResult> callable) { return call(callable, IMMEDIATE_EXECUTOR); } /** * Creates a task that completes when all of the provided tasks are complete. * * @param tasks The tasks that the return value will wait for before completing. * @return A Task that will resolve to {@code Void} when all the tasks are resolved. If a single * task fails, it will resolve to that {@link java.lang.Exception}. If multiple tasks fail, it * will resolve to an {@link AggregateException} of all the {@link java.lang.Exception}s. */ public static Task<Void> whenAll(Collection<? extends Task<?>> tasks) { if (tasks.size() == 0) { return Task.forResult(null); } final Task<Void>.TaskCompletionSource allFinished = Task.create(); final ArrayList<Exception> causes = new ArrayList<Exception>(); final Object errorLock = new Object(); final AtomicInteger count = new AtomicInteger(tasks.size()); final AtomicBoolean isCancelled = new AtomicBoolean(false); for (Task<?> task : tasks) { @SuppressWarnings("unchecked") Task<Object> t = (Task<Object>) task; t.continueWith(new Continuation<Object, Void>() { @Override public Void then(Task<Object> task) { if (task.isFaulted()) { synchronized (errorLock) { causes.add(task.getError()); } } if (task.isCancelled()) { isCancelled.set(true); } if (count.decrementAndGet() == 0) { if (causes.size() != 0) { if (causes.size() == 1) { allFinished.setError(causes.get(0)); } else { Throwable[] throwables = causes.toArray(new Throwable[causes.size()]); Exception error = new AggregateException( String.format("There were %d exceptions.", causes.size()), throwables); allFinished.setError(error); } } else if (isCancelled.get()) { allFinished.setCancelled(); } else { allFinished.setResult(null); } } return null; } }); } return allFinished.getTask(); } /** * Continues a task with the equivalent of a Task-based while loop, where the body of the loop is * a task continuation. */ public Task<Void> continueWhile(Callable<Boolean> predicate, Continuation<Void, Task<Void>> continuation) { return continueWhile(predicate, continuation, IMMEDIATE_EXECUTOR); } /** * Continues a task with the equivalent of a Task-based while loop, where the body of the loop is * a task continuation. */ public Task<Void> continueWhile(final Callable<Boolean> predicate, final Continuation<Void, Task<Void>> continuation, final Executor executor) { final Capture<Continuation<Void, Task<Void>>> predicateContinuation = new Capture<Continuation<Void, Task<Void>>>(); predicateContinuation.set(new Continuation<Void, Task<Void>>() { @Override public Task<Void> then(Task<Void> task) throws Exception { if (predicate.call()) { return Task.<Void> forResult(null).onSuccessTask(continuation, executor) .onSuccessTask(predicateContinuation.get(), executor); } return Task.forResult(null); } }); return makeVoid().continueWithTask(predicateContinuation.get(), executor); } /** * Adds a continuation that will be scheduled using the executor, returning a new task that * completes after the continuation has finished running. This allows the continuation to be * scheduled on different thread. */ public <TContinuationResult> Task<TContinuationResult> continueWith( final Continuation<TResult, TContinuationResult> continuation, final Executor executor) { boolean completed; final Task<TContinuationResult>.TaskCompletionSource tcs = Task.create(); synchronized (lock) { completed = this.isCompleted(); if (!completed) { this.continuations.add(new Continuation<TResult, Void>() { @Override public Void then(Task<TResult> task) { completeImmediately(tcs, continuation, task, executor); return null; } }); } } if (completed) { completeImmediately(tcs, continuation, this, executor); } return tcs.getTask(); } /** * Adds a synchronous continuation to this task, returning a new task that completes after the * continuation has finished running. */ public <TContinuationResult> Task<TContinuationResult> continueWith( Continuation<TResult, TContinuationResult> continuation) { return continueWith(continuation, IMMEDIATE_EXECUTOR); } /** * Adds an Task-based continuation to this task that will be scheduled using the executor, * returning a new task that completes after the task returned by the continuation has completed. */ public <TContinuationResult> Task<TContinuationResult> continueWithTask( final Continuation<TResult, Task<TContinuationResult>> continuation, final Executor executor) { boolean completed; final Task<TContinuationResult>.TaskCompletionSource tcs = Task.create(); synchronized (lock) { completed = this.isCompleted(); if (!completed) { this.continuations.add(new Continuation<TResult, Void>() { @Override public Void then(Task<TResult> task) { completeAfterTask(tcs, continuation, task, executor); return null; } }); } } if (completed) { completeAfterTask(tcs, continuation, this, executor); } return tcs.getTask(); } /** * Adds an asynchronous continuation to this task, returning a new task that completes after the * task returned by the continuation has completed. */ public <TContinuationResult> Task<TContinuationResult> continueWithTask( Continuation<TResult, Task<TContinuationResult>> continuation) { return continueWithTask(continuation, IMMEDIATE_EXECUTOR); } /** * Runs a continuation when a task completes successfully, forwarding along * {@link java.lang.Exception} or cancellation. */ public <TContinuationResult> Task<TContinuationResult> onSuccess( final Continuation<TResult, TContinuationResult> continuation, Executor executor) { return continueWithTask(new Continuation<TResult, Task<TContinuationResult>>() { @Override public Task<TContinuationResult> then(Task<TResult> task) { if (task.isFaulted()) { return Task.forError(task.getError()); } else if (task.isCancelled()) { return Task.cancelled(); } else { return task.continueWith(continuation); } } }, executor); } /** * Runs a continuation when a task completes successfully, forwarding along * {@link java.lang.Exception}s or cancellation. */ public <TContinuationResult> Task<TContinuationResult> onSuccess( final Continuation<TResult, TContinuationResult> continuation) { return onSuccess(continuation, IMMEDIATE_EXECUTOR); } /** * Runs a continuation when a task completes successfully, forwarding along * {@link java.lang.Exception}s or cancellation. */ public <TContinuationResult> Task<TContinuationResult> onSuccessTask( final Continuation<TResult, Task<TContinuationResult>> continuation, Executor executor) { return continueWithTask(new Continuation<TResult, Task<TContinuationResult>>() { @Override public Task<TContinuationResult> then(Task<TResult> task) { if (task.isFaulted()) { return Task.forError(task.getError()); } else if (task.isCancelled()) { return Task.cancelled(); } else { return task.continueWithTask(continuation); } } }, executor); } /** * Runs a continuation when a task completes successfully, forwarding along * {@link java.lang.Exception}s or cancellation. */ public <TContinuationResult> Task<TContinuationResult> onSuccessTask( final Continuation<TResult, Task<TContinuationResult>> continuation) { return onSuccessTask(continuation, IMMEDIATE_EXECUTOR); } /** * Handles the non-async (i.e. the continuation doesn't return a Task) continuation case, passing * the results of the given Task through to the given continuation and using the results of that * call to set the result of the TaskContinuationSource. * * @param tcs * The TaskContinuationSource that will be orchestrated by this call. * @param continuation * The non-async continuation. * @param task * The task being completed. * @param executor * The executor to use when running the continuation (allowing the continuation to be * scheduled on a different thread). */ private static <TContinuationResult, TResult> void completeImmediately( final Task<TContinuationResult>.TaskCompletionSource tcs, final Continuation<TResult, TContinuationResult> continuation, final Task<TResult> task, Executor executor) { executor.execute(new Runnable() { @Override public void run() { try { TContinuationResult result = continuation.then(task); tcs.setResult(result); } catch (Exception e) { tcs.setError(e); } } }); } /** * Handles the async (i.e. the continuation does return a Task) continuation case, passing the * results of the given Task through to the given continuation to get a new Task. The * TaskCompletionSource's results are only set when the new Task has completed, unwrapping the * results of the task returned by the continuation. * * @param tcs * The TaskContinuationSource that will be orchestrated by this call. * @param continuation * The async continuation. * @param task * The task being completed. * @param executor * The executor to use when running the continuation (allowing the continuation to be * scheduled on a different thread). */ private static <TContinuationResult, TResult> void completeAfterTask( final Task<TContinuationResult>.TaskCompletionSource tcs, final Continuation<TResult, Task<TContinuationResult>> continuation, final Task<TResult> task, final Executor executor) { executor.execute(new Runnable() { @Override public void run() { try { Task<TContinuationResult> result = continuation.then(task); if (result == null) { tcs.setResult(null); } else { result.continueWith(new Continuation<TContinuationResult, Void>() { @Override public Void then(Task<TContinuationResult> task) { if (task.isCancelled()) { tcs.setCancelled(); } else if (task.isFaulted()) { tcs.setError(task.getError()); } else { tcs.setResult(task.getResult()); } return null; } }); } } catch (Exception e) { tcs.setError(e); } } }); } private void runContinuations() { synchronized (lock) { for (Continuation<TResult, ?> continuation : continuations) { try { continuation.then(this); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } continuations = null; } } /** * Allows safe orchestration of a task's completion, preventing the consumer from prematurely * completing the task. Essentially, it represents the producer side of a Task<TResult>, providing * access to the consumer side through the getTask() method while isolating the Task's completion * mechanisms from the consumer. */ public class TaskCompletionSource { private TaskCompletionSource() { } /** * @return the Task associated with this TaskCompletionSource. */ public Task<TResult> getTask() { return Task.this; } /** * Sets the cancelled flag on the Task if the Task hasn't already been completed. */ public boolean trySetCancelled() { synchronized (lock) { if (complete) { return false; } complete = true; cancelled = true; lock.notifyAll(); runContinuations(); return true; } } /** * Sets the result on the Task if the Task hasn't already been completed. */ public boolean trySetResult(TResult result) { synchronized (lock) { if (complete) { return false; } complete = true; Task.this.result = result; lock.notifyAll(); runContinuations(); return true; } } /** * Sets the error on the Task if the Task hasn't already been completed. */ public boolean trySetError(Exception error) { synchronized (lock) { if (complete) { return false; } complete = true; Task.this.error = error; lock.notifyAll(); runContinuations(); return true; } } /** * Sets the cancelled flag on the task, throwing if the Task has already been completed. */ public void setCancelled() { if (!trySetCancelled()) { throw new IllegalStateException("Cannot cancel a completed task."); } } /** * Sets the result of the Task, throwing if the Task has already been completed. */ public void setResult(TResult result) { if (!trySetResult(result)) { throw new IllegalStateException("Cannot set the result of a completed task."); } } /** * Sets the error of the Task, throwing if the Task has already been completed. */ public void setError(Exception error) { if (!trySetError(error)) { throw new IllegalStateException("Cannot set the error on a completed task."); } } } }