/******************************************************************************* * Copyright 2013 Ray Tsang * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package jdeferred.android; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import org.jdeferred.DeferredFutureTask; import org.jdeferred.DeferredManager; import org.jdeferred.Promise; import org.jdeferred.impl.DefaultDeferredManager; import org.jdeferred.multiple.MasterDeferredObject; import org.jdeferred.multiple.MasterProgress; import org.jdeferred.multiple.MultipleResults; import org.jdeferred.multiple.OneReject; import android.annotation.SuppressLint; import android.os.AsyncTask; import android.os.Build; /** * This DeferredManager is designed to execute deferred tasks in the background, * but also executes callbacks (e.g., done, fail, progress, and always) in the UI thread. * This is important because only UI thread executions can update UI elements! * * You can use {@link DeferredAsyncTask} to write in the more familiar Android {@link android.os.AsyncTask} API * and still being able to take advantage of {@link Promise} chaining. * * Even more powerful, you can also use {@link Promise}, {@link Runnable}, {@link java.util.concurrent.Callable}, * and any other types supported by {@link DeferredManager}. This implementation will hand off * callbacks to UI thread automatically. * * @author Ray Tsang * */ public class AndroidDeferredManager extends DefaultDeferredManager { private static Void[] EMPTY_PARAMS = new Void[]{}; public AndroidDeferredManager() { super(); } public AndroidDeferredManager(ExecutorService executorService) { super(executorService); } /** * Return a {@link Promise} for the {@link DeferredAsyncTask}. * This can also automatically execute the task in background depending on * {@link DeferredAsyncTask#getStartPolicy()} and/or {@link DefaultDeferredManager#isAutoSubmit()}. * * Prior to Android Honeycomb (API 11), {@link android.os.AsyncTask#execute(Object...)} would be * executed in the background concurrently in a thread pool, but starting with Honeycomb, * {@link android.os.AsyncTask#execute(Object...)} will execute the background task serially. To achieve * older behavior, developer need to use {@link android.os.AsyncTask#executeOnExecutor(java.util.concurrent.Executor, Object...)}. * * This method will always execute task in background concurrently if the task should be executed/submitted automatically. * Hence, when using this method on Android API prior to Honeycomb, the task will be executed * using {@link android.os.AsyncTask#execute(Object...)}. On Android API version starting from Honeycomb, * this method will execute with @see {@link android.os.AsyncTask#executeOnExecutor(java.util.concurrent.Executor, Object...)} * using {@link java.util.concurrent.Executor} from {@link #getExecutorService()} * * @param task {@link DeferredAsyncTask} to run in the background * @return {@link DeferredAsyncTask#promise()} * @see {@link android.os.AsyncTask#execute(Object...)} * @see {@link android.os.AsyncTask#executeOnExecutor(java.util.concurrent.Executor, Object...)} */ @SuppressLint("NewApi") public <Progress, Result> Promise<Result, Throwable, Progress> when( DeferredAsyncTask<Void, Progress, Result> task) { if (task.getStartPolicy() == StartPolicy.AUTO || (task.getStartPolicy() == StartPolicy.DEFAULT && isAutoSubmit())) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { task.executeOnExecutor(getExecutorService(), EMPTY_PARAMS); } else { task.execute(EMPTY_PARAMS); } } return task.promise(); } @SuppressWarnings("rawtypes") public Promise<MultipleResults, OneReject, MasterProgress> when( DeferredAsyncTask<Void, ?, ?> ... tasks) { assertNotEmpty(tasks); Promise[] promises = new Promise[tasks.length]; for (int i = 0; i < tasks.length; i++) { promises[i] = when(tasks[i]); } return when(promises); } @SuppressWarnings("rawtypes") public Promise<MultipleResults, OneReject, MasterProgress> when(AndroidExecutionScope scope, DeferredAsyncTask<Void, ?, ?> ... tasks) { assertNotEmpty(tasks); Promise[] promises = new Promise[tasks.length]; for (int i = 0; i < tasks.length; i++) { promises[i] = when(tasks[i]); } return when(scope, promises); } /** * Wrap with {@link AndroidDeferredObject} so that callbacks can be executed in UI thread. * This method is called by a number of other when(...) methods. Effectively, at least these * methods will also be wrapped by {@link AndroidDeferredObject}: * <ul> * <li>{@link DeferredManager#when(java.util.concurrent.Callable)}</li> * <li>{@link DeferredManager#when(java.util.concurrent.Callable...)}</li> * <li>{@link DeferredManager#when(Runnable)}</li> * <li>{@link DeferredManager#when(Runnable..)}</li> * <li>{@link DeferredManager#when(java.util.concurrent.Future)}</li> * <li>{@link DeferredManager#when(java.util.concurrent.Future...)}</li> * <li>{@link DeferredManager#when(org.jdeferred.DeferredRunnable...)}</li> * <li>{@link DeferredManager#when(org.jdeferred.DeferredRunnable)}</li> * <li>{@link DeferredManager#when(org.jdeferred.DeferredCallable...)}</li> * <li>{@link DeferredManager#when(org.jdeferred.DeferredCallable)}</li> * <li>{@link DeferredManager#when(DeferredFutureTask...)}</li> * </ul> */ @Override public <D, P> Promise<D, Throwable, P> when(DeferredFutureTask<D, P> task) { return new AndroidDeferredObject<D, Throwable, P>(super.when(task)).promise(); } /** * If a non-Android friendly promise is passed in, wrap it with {@link AndroidDeferredObject} * so that callbacks can be executed in the UI thread. */ @Override public <D, F, P> Promise<D, F, P> when(Promise<D, F, P> promise) { if (promise instanceof AndroidDeferredObject) { return promise; } return new AndroidDeferredObject<D, F, P>(promise).promise(); } /** * If a non-Android friendly promise is passed in, wrap it with {@link AndroidDeferredObject} * so that callbacks can be executed in the corresponding execution scope. * @param scope Whether to execute in UI thread or Background thread * @param promise A promise * @return A promise wrapped in @{link AndroidDeferredObject} */ public <D, F, P> Promise<D, F, P> when(Promise<D, F, P> promise, AndroidExecutionScope scope) { if (promise instanceof AndroidDeferredObject) { return promise; } return new AndroidDeferredObject<D, F, P>(promise, scope).promise(); } /** * Wraps {@link MasterDeferredObject} with {@link AndroidDeferredObject} so that callbacks can * be executed in UI thread. */ @SuppressWarnings({ "rawtypes" }) @Override public Promise<MultipleResults, OneReject, MasterProgress> when(Promise... promises) { return new AndroidDeferredObject<MultipleResults, OneReject, MasterProgress> (super.when(promises)).promise(); } /** * Wraps {@link MasterDeferredObject} with with {@link AndroidDeferredObject} * so that callbacks can be executed in the corresponding execution scope. * @return A promise wrapped in @{link AndroidDeferredObject} */ @SuppressWarnings({ "rawtypes" }) public Promise<MultipleResults, OneReject, MasterProgress> when(AndroidExecutionScope scope, Promise... promises) { return new AndroidDeferredObject<MultipleResults, OneReject, MasterProgress> (super.when(promises), scope).promise(); } }