278 lines
9.0 KiB
Java
278 lines
9.0 KiB
Java
/*
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
*
|
|
* 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 com.android.emailcommon.utility;
|
|
|
|
import android.os.AsyncTask;
|
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedList;
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/**
|
|
* {@link AsyncTask} substitution for the email app.
|
|
*
|
|
* Modeled after {@link AsyncTask}; the basic usage is the same, with extra features:
|
|
* - Bulk cancellation of multiple tasks. This is mainly used by UI to cancel pending tasks
|
|
* in onDestroy() or similar places.
|
|
* - Instead of {@link AsyncTask#onPostExecute}, it has {@link #onSuccess(Object)}, as the
|
|
* regular {@link AsyncTask#onPostExecute} is a bit hard to predict when it'll be called and
|
|
* when it won't.
|
|
*
|
|
* Note this class is missing some of the {@link AsyncTask} features, e.g. it lacks
|
|
* {@link AsyncTask#onProgressUpdate}. Add these when necessary.
|
|
*/
|
|
public abstract class EmailAsyncTask<Params, Progress, Result> {
|
|
private static final Executor SERIAL_EXECUTOR = AsyncTask.SERIAL_EXECUTOR;
|
|
private static final Executor PARALLEL_EXECUTOR = AsyncTask.THREAD_POOL_EXECUTOR;
|
|
|
|
/**
|
|
* Tracks {@link EmailAsyncTask}.
|
|
*
|
|
* Call {@link #cancelAllInterrupt()} to cancel all tasks registered.
|
|
*/
|
|
public static class Tracker {
|
|
private final LinkedList<EmailAsyncTask<?, ?, ?>> mTasks =
|
|
new LinkedList<EmailAsyncTask<?, ?, ?>>();
|
|
|
|
private void add(EmailAsyncTask<?, ?, ?> task) {
|
|
synchronized (mTasks) {
|
|
mTasks.add(task);
|
|
}
|
|
}
|
|
|
|
private void remove(EmailAsyncTask<?, ?, ?> task) {
|
|
synchronized (mTasks) {
|
|
mTasks.remove(task);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancel all registered tasks.
|
|
*/
|
|
@VisibleForTesting
|
|
public void cancelAllInterrupt() {
|
|
synchronized (mTasks) {
|
|
for (EmailAsyncTask<?, ?, ?> task : mTasks) {
|
|
task.cancel(true);
|
|
}
|
|
mTasks.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancel all instances of the same class as {@code current} other than
|
|
* {@code current} itself.
|
|
*/
|
|
/* package */ void cancelOthers(EmailAsyncTask<?, ?, ?> current) {
|
|
final Class<?> clazz = current.getClass();
|
|
synchronized (mTasks) {
|
|
final ArrayList<EmailAsyncTask<?, ?, ?>> toRemove =
|
|
new ArrayList<EmailAsyncTask<?, ?, ?>>();
|
|
for (EmailAsyncTask<?, ?, ?> task : mTasks) {
|
|
if ((task != current) && task.getClass().equals(clazz)) {
|
|
task.cancel(true);
|
|
toRemove.add(task);
|
|
}
|
|
}
|
|
for (EmailAsyncTask<?, ?, ?> task : toRemove) {
|
|
mTasks.remove(task);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* package */ int getTaskCountForTest() {
|
|
return mTasks.size();
|
|
}
|
|
|
|
/* package */ boolean containsTaskForTest(EmailAsyncTask<?, ?, ?> task) {
|
|
return mTasks.contains(task);
|
|
}
|
|
}
|
|
|
|
private final Tracker mTracker;
|
|
|
|
private static class InnerTask<Params2, Progress2, Result2>
|
|
extends AsyncTask<Params2, Progress2, Result2> {
|
|
private final EmailAsyncTask<Params2, Progress2, Result2> mOwner;
|
|
|
|
public InnerTask(EmailAsyncTask<Params2, Progress2, Result2> owner) {
|
|
mOwner = owner;
|
|
}
|
|
|
|
@Override
|
|
protected Result2 doInBackground(Params2... params) {
|
|
return mOwner.doInBackground(params);
|
|
}
|
|
|
|
@Override
|
|
public void onCancelled(Result2 result) {
|
|
mOwner.unregisterSelf();
|
|
mOwner.onCancelled(result);
|
|
}
|
|
|
|
@Override
|
|
public void onPostExecute(Result2 result) {
|
|
mOwner.unregisterSelf();
|
|
if (mOwner.mCancelled) {
|
|
mOwner.onCancelled(result);
|
|
} else {
|
|
mOwner.onSuccess(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
private final InnerTask<Params, Progress, Result> mInnerTask;
|
|
private volatile boolean mCancelled;
|
|
|
|
public EmailAsyncTask(Tracker tracker) {
|
|
mTracker = tracker;
|
|
if (mTracker != null) {
|
|
mTracker.add(this);
|
|
}
|
|
mInnerTask = new InnerTask<Params, Progress, Result>(this);
|
|
}
|
|
|
|
/* package */ final void unregisterSelf() {
|
|
if (mTracker != null) {
|
|
mTracker.remove(this);
|
|
}
|
|
}
|
|
|
|
/** @see AsyncTask#doInBackground */
|
|
protected abstract Result doInBackground(Params... params);
|
|
|
|
|
|
/** @see AsyncTask#cancel(boolean) */
|
|
public final void cancel(boolean mayInterruptIfRunning) {
|
|
mCancelled = true;
|
|
mInnerTask.cancel(mayInterruptIfRunning);
|
|
}
|
|
|
|
/** @see AsyncTask#onCancelled */
|
|
protected void onCancelled(Result result) {
|
|
}
|
|
|
|
/**
|
|
* Similar to {@link AsyncTask#onPostExecute}, but this will never be executed if
|
|
* {@link #cancel(boolean)} has been called before its execution, even if
|
|
* {@link #doInBackground(Object...)} has completed when cancelled.
|
|
*
|
|
* @see AsyncTask#onPostExecute
|
|
*/
|
|
protected void onSuccess(Result result) {
|
|
}
|
|
|
|
/**
|
|
* execute on {@link #PARALLEL_EXECUTOR}
|
|
*
|
|
* @see AsyncTask#execute
|
|
*/
|
|
public final EmailAsyncTask<Params, Progress, Result> executeParallel(Params... params) {
|
|
return executeInternal(PARALLEL_EXECUTOR, false, params);
|
|
}
|
|
|
|
/**
|
|
* execute on {@link #SERIAL_EXECUTOR}
|
|
*
|
|
* @see AsyncTask#execute
|
|
*/
|
|
public final EmailAsyncTask<Params, Progress, Result> executeSerial(Params... params) {
|
|
return executeInternal(SERIAL_EXECUTOR, false, params);
|
|
}
|
|
|
|
/**
|
|
* Cancel all previously created instances of the same class tracked by the same
|
|
* {@link Tracker}, and then {@link #executeParallel}.
|
|
*/
|
|
public final EmailAsyncTask<Params, Progress, Result> cancelPreviousAndExecuteParallel(
|
|
Params... params) {
|
|
return executeInternal(PARALLEL_EXECUTOR, true, params);
|
|
}
|
|
|
|
/**
|
|
* Cancel all previously created instances of the same class tracked by the same
|
|
* {@link Tracker}, and then {@link #executeSerial}.
|
|
*/
|
|
public final EmailAsyncTask<Params, Progress, Result> cancelPreviousAndExecuteSerial(
|
|
Params... params) {
|
|
return executeInternal(SERIAL_EXECUTOR, true, params);
|
|
}
|
|
|
|
private EmailAsyncTask<Params, Progress, Result> executeInternal(Executor executor,
|
|
boolean cancelPrevious, Params... params) {
|
|
if (cancelPrevious) {
|
|
if (mTracker == null) {
|
|
throw new IllegalStateException();
|
|
} else {
|
|
mTracker.cancelOthers(this);
|
|
}
|
|
}
|
|
mInnerTask.executeOnExecutor(executor, params);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Runs a {@link Runnable} in a bg thread, using {@link #PARALLEL_EXECUTOR}.
|
|
*/
|
|
public static EmailAsyncTask<Void, Void, Void> runAsyncParallel(Runnable runnable) {
|
|
return runAsyncInternal(PARALLEL_EXECUTOR, runnable);
|
|
}
|
|
|
|
/**
|
|
* Runs a {@link Runnable} in a bg thread, using {@link #SERIAL_EXECUTOR}.
|
|
*/
|
|
public static EmailAsyncTask<Void, Void, Void> runAsyncSerial(Runnable runnable) {
|
|
return runAsyncInternal(SERIAL_EXECUTOR, runnable);
|
|
}
|
|
|
|
private static EmailAsyncTask<Void, Void, Void> runAsyncInternal(Executor executor,
|
|
final Runnable runnable) {
|
|
EmailAsyncTask<Void, Void, Void> task = new EmailAsyncTask<Void, Void, Void>(null) {
|
|
@Override
|
|
protected Void doInBackground(Void... params) {
|
|
runnable.run();
|
|
return null;
|
|
}
|
|
};
|
|
return task.executeInternal(executor, false, (Void[]) null);
|
|
}
|
|
|
|
/**
|
|
* Wait until {@link #doInBackground} finishes and returns the results of the computation.
|
|
*
|
|
* @see AsyncTask#get
|
|
*/
|
|
public final Result get() throws InterruptedException, ExecutionException {
|
|
return mInnerTask.get();
|
|
}
|
|
|
|
/* package */ final Result callDoInBackgroundForTest(Params... params) {
|
|
return mInnerTask.doInBackground(params);
|
|
}
|
|
|
|
/* package */ final void callOnCancelledForTest(Result result) {
|
|
mInnerTask.onCancelled(result);
|
|
}
|
|
|
|
/* package */ final void callOnPostExecuteForTest(Result result) {
|
|
mInnerTask.onPostExecute(result);
|
|
}
|
|
}
|