Merge "Improve EmailAsyncTask"

This commit is contained in:
Makoto Onuki 2011-03-21 17:31:48 -07:00 committed by Android (Google) Code Review
commit ca64197427
9 changed files with 237 additions and 109 deletions

View File

@ -18,14 +18,16 @@ package com.android.emailcommon.utility;
import android.os.AsyncTask; import android.os.AsyncTask;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
/** /**
* {@link AsyncTask} substitution for the email app. * {@link AsyncTask} substitution for the email app.
* *
* Modeled after {@link AsyncTask}; the basic usage is the same, with extra features: * 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 cancell pending tasks * - Bulk cancellation of multiple tasks. This is mainly used by UI to cancel pending tasks
* in onDestroy() or similar places. * in onDestroy() or similar places.
* - More features to come... * - More features to come...
* *
@ -33,6 +35,9 @@ import java.util.concurrent.ExecutionException;
* {@link AsyncTask#onProgressUpdate}. Add these when necessary. * {@link AsyncTask#onProgressUpdate}. Add these when necessary.
*/ */
public abstract class EmailAsyncTask<Params, Progress, Result> { 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}. * Tracks {@link EmailAsyncTask}.
* *
@ -66,9 +71,34 @@ public abstract class EmailAsyncTask<Params, Progress, Result> {
} }
} }
/**
* 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() { /* package */ int getTaskCountForTest() {
return mTasks.size(); return mTasks.size();
} }
/* package */ boolean containsTaskForTest(EmailAsyncTask<?, ?, ?> task) {
return mTasks.contains(task);
}
} }
private final Tracker mTracker; private final Tracker mTracker;
@ -109,46 +139,127 @@ public abstract class EmailAsyncTask<Params, Progress, Result> {
mInnerTask = new InnerTask<Params, Progress, Result>(this); mInnerTask = new InnerTask<Params, Progress, Result>(this);
} }
/* package */ void unregisterSelf() { /* package */ final void unregisterSelf() {
if (mTracker != null) { if (mTracker != null) {
mTracker.remove(this); mTracker.remove(this);
} }
} }
/** @see AsyncTask#doInBackground */
protected abstract Result doInBackground(Params... params); protected abstract Result doInBackground(Params... params);
/** @see AsyncTask#cancel(boolean) */
public final boolean cancel(boolean mayInterruptIfRunning) { public final boolean cancel(boolean mayInterruptIfRunning) {
return mInnerTask.cancel(mayInterruptIfRunning); return mInnerTask.cancel(mayInterruptIfRunning);
} }
/** @see AsyncTask#onCancelled */
protected void onCancelled(Result result) { protected void onCancelled(Result result) {
} }
/** @see AsyncTask#onPostExecute */
protected void onPostExecute(Result result) { protected void onPostExecute(Result result) {
} }
public final EmailAsyncTask<Params, Progress, Result> execute(Params... params) { /**
mInnerTask.execute(params); * 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 final 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; return this;
} }
public final Result get() throws InterruptedException, ExecutionException { /**
return mInnerTask.get(); * 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.
*
* @see AsyncTask#get
*/
public final void waitForFinish() throws InterruptedException, ExecutionException {
mInnerTask.get();
}
/** @see AsyncTask#isCancelled */
public final boolean isCancelled() { public final boolean isCancelled() {
return mInnerTask.isCancelled(); return mInnerTask.isCancelled();
} }
/* package */ Result callDoInBackgroundForTest(Params... params) { /* package */ final Result callDoInBackgroundForTest(Params... params) {
return mInnerTask.doInBackground(params); return mInnerTask.doInBackground(params);
} }
/* package */ void callOnCancelledForTest(Result result) { /* package */ final void callOnCancelledForTest(Result result) {
mInnerTask.onCancelled(result); mInnerTask.onCancelled(result);
} }
/* package */ void callOnPostExecuteForTest(Result result) { /* package */ final void callOnPostExecuteForTest(Result result) {
mInnerTask.onPostExecute(result); mInnerTask.onPostExecute(result);
} }
} }

View File

@ -573,14 +573,18 @@ public class Utility {
* Run {@code r} on a worker thread, returning the AsyncTask * Run {@code r} on a worker thread, returning the AsyncTask
* @return the AsyncTask; this is primarily for use by unit tests, which require the * @return the AsyncTask; this is primarily for use by unit tests, which require the
* result of the task * result of the task
*
* @deprecated use {@link EmailAsyncTask#runAsyncParallel} or
* {@link EmailAsyncTask#runAsyncSerial}
*/ */
@Deprecated
public static AsyncTask<Void, Void, Void> runAsync(final Runnable r) { public static AsyncTask<Void, Void, Void> runAsync(final Runnable r) {
return new AsyncTask<Void, Void, Void>() { return new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) { @Override protected Void doInBackground(Void... params) {
r.run(); r.run();
return null; return null;
} }
}.execute(); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
/** /**

View File

@ -19,12 +19,11 @@ package com.android.email.activity;
import com.android.email.R; import com.android.email.R;
import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.EmailAsyncTask;
import android.app.ListActivity; import android.app.ListActivity;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.view.View; import android.view.View;
@ -43,7 +42,7 @@ import android.widget.SimpleCursorAdapter;
public class AccountShortcutPicker extends ListActivity public class AccountShortcutPicker extends ListActivity
implements OnClickListener, OnItemClickListener { implements OnClickListener, OnItemClickListener {
private AccountTask mAccountTask; private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
/** /**
* Support for list adapter * Support for list adapter
@ -74,16 +73,14 @@ public class AccountShortcutPicker extends ListActivity
listView.setOnItemClickListener(this); listView.setOnItemClickListener(this);
listView.setItemsCanFocus(false); listView.setItemsCanFocus(false);
mAccountTask = new AccountTask(); new AccountTask().executeParallel();
mAccountTask.execute();
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
// Cleanup running async task (if any) // Cleanup running async task (if any)
Utility.cancelTaskInterrupt(mAccountTask); mTaskTracker.cancellAllInterrupt();
mAccountTask = null;
// Cleanup accounts cursor (if any) // Cleanup accounts cursor (if any)
SimpleCursorAdapter adapter = (SimpleCursorAdapter) getListAdapter(); SimpleCursorAdapter adapter = (SimpleCursorAdapter) getListAdapter();
if (adapter != null) { if (adapter != null) {
@ -114,7 +111,10 @@ public class AccountShortcutPicker extends ListActivity
/** /**
* Load the accounts and create the adapter. * Load the accounts and create the adapter.
*/ */
private class AccountTask extends AsyncTask<Void, Void, Cursor> { private class AccountTask extends EmailAsyncTask<Void, Void, Cursor> {
public AccountTask() {
super(mTaskTracker);
}
@Override @Override
protected Cursor doInBackground(Void... params) { protected Cursor doInBackground(Void... params) {

View File

@ -34,6 +34,7 @@ import com.android.emailcommon.provider.EmailContent.BodyColumns;
import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns; import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.utility.AttachmentUtilities; import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
import android.app.ActionBar; import android.app.ActionBar;
@ -48,7 +49,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
@ -157,8 +157,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
private Controller mController; private Controller mController;
private boolean mDraftNeedsSaving; private boolean mDraftNeedsSaving;
private boolean mMessageLoaded; private boolean mMessageLoaded;
private AsyncTask<Long, Void, Attachment[]> mLoadAttachmentsTask; private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
private AsyncTask<Void, Void, Object[]> mLoadMessageTask;
private EmailAddressAdapter mAddressAdapterTo; private EmailAddressAdapter mAddressAdapterTo;
private EmailAddressAdapter mAddressAdapterCc; private EmailAddressAdapter mAddressAdapterCc;
@ -336,7 +335,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
// Otherwise, handle the internal cases (Message Composer invoked from within app) // Otherwise, handle the internal cases (Message Composer invoked from within app)
long messageId = draftId != -1 ? draftId : intent.getLongExtra(EXTRA_MESSAGE_ID, -1); long messageId = draftId != -1 ? draftId : intent.getLongExtra(EXTRA_MESSAGE_ID, -1);
if (messageId != -1) { if (messageId != -1) {
mLoadMessageTask = new LoadMessageTask(messageId).execute(); new LoadMessageTask(messageId).executeParallel();
} else { } else {
setAccount(intent); setAccount(intent);
// Since this is a new message, we don't need to call LoadMessageTask. // Since this is a new message, we don't need to call LoadMessageTask.
@ -394,10 +393,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mQuotedText.destroy(); mQuotedText.destroy();
mQuotedText = null; mQuotedText = null;
Utility.cancelTaskInterrupt(mLoadAttachmentsTask); mTaskTracker.cancellAllInterrupt();
mLoadAttachmentsTask = null;
Utility.cancelTaskInterrupt(mLoadMessageTask);
mLoadMessageTask = null;
if (mAddressAdapterTo != null) { if (mAddressAdapterTo != null) {
mAddressAdapterTo.close(); mAddressAdapterTo.close();
@ -608,10 +604,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mAddressAdapterBcc = new EmailAddressAdapter(this); mAddressAdapterBcc = new EmailAddressAdapter(this);
} }
private class LoadMessageTask extends AsyncTask<Void, Void, Object[]> { private class LoadMessageTask extends EmailAsyncTask<Void, Void, Object[]> {
private final long mMessageId; private final long mMessageId;
public LoadMessageTask(long messageId) { public LoadMessageTask(long messageId) {
super(mTaskTracker);
mMessageId = messageId; mMessageId = messageId;
} }
@ -686,7 +683,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
} else { } else {
mSource = message; mSource = message;
} }
mLoadAttachmentsTask = new AsyncTask<Long, Void, Attachment[]>() { new EmailAsyncTask<Long, Void, Attachment[]>(mTaskTracker) {
@Override @Override
protected Attachment[] doInBackground(Long... messageIds) { protected Attachment[] doInBackground(Long... messageIds) {
return Attachment.restoreAttachmentsWithMessageId(MessageCompose.this, return Attachment.restoreAttachmentsWithMessageId(MessageCompose.this,
@ -716,7 +713,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
addAttachment(attachment, allowDelete); addAttachment(attachment, allowDelete);
} }
} }
}.execute(message.mId); }.executeParallel(message.mId);
} else if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction)) { } else if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction)) {
mSource = message; mSource = message;
} else if (Email.LOGD) { } else if (Email.LOGD) {
@ -909,10 +906,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
} }
} }
private class SendOrSaveMessageTask extends AsyncTask<Void, Void, Void> { private class SendOrSaveMessageTask extends EmailAsyncTask<Void, Void, Void> {
private final boolean mSend; private final boolean mSend;
public SendOrSaveMessageTask(boolean send) { public SendOrSaveMessageTask(boolean send) {
super(null /* DO NOT cancel in onDestroy */);
if (send && ActivityManager.isUserAMonkey()) { if (send && ActivityManager.isUserAMonkey()) {
Log.d(Logging.LOG_TAG, "Inhibiting send while monkey is in charge."); Log.d(Logging.LOG_TAG, "Inhibiting send while monkey is in charge.");
send = false; send = false;
@ -994,9 +992,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
sSaveInProgress = false; sSaveInProgress = false;
sSaveInProgressCondition.notify(); sSaveInProgressCondition.notify();
} }
if (isCancelled()) {
return;
}
// Don't display the toast if the user is just changing the orientation // Don't display the toast if the user is just changing the orientation
if (!mSend && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) { if (!mSend && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
Toast.makeText(MessageCompose.this, R.string.message_saved_toast, Toast.makeText(MessageCompose.this, R.string.message_saved_toast,
@ -1020,7 +1015,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
synchronized (sSaveInProgressCondition) { synchronized (sSaveInProgressCondition) {
sSaveInProgress = true; sSaveInProgress = true;
} }
new SendOrSaveMessageTask(send).execute(); new SendOrSaveMessageTask(send).executeParallel();
} }
private void saveIfNeeded() { private void saveIfNeeded() {
@ -1252,16 +1247,13 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mAttachments.removeView(attachmentView); mAttachments.removeView(attachmentView);
updateAttachmentContainer(); updateAttachmentContainer();
if (attachment.mMessageKey == mDraft.mId && attachment.isSaved()) { if (attachment.mMessageKey == mDraft.mId && attachment.isSaved()) {
// The following async task for deleting attachments: final long attachmentId = attachment.mId;
// - can be started multiple times in parallel (to delete multiple attachments). EmailAsyncTask.runAsyncParallel(new Runnable() {
// - need not be interrupted on activity exit, instead should run to completion.
new AsyncTask<Long, Void, Void>() {
@Override @Override
protected Void doInBackground(Long... attachmentIds) { public void run() {
mController.deleteAttachment(attachmentIds[0]); mController.deleteAttachment(attachmentId);
return null;
} }
}.execute(attachment.mId); });
} }
setDraftNeedsSaving(true); setDraftNeedsSaving(true);
} }

View File

@ -28,6 +28,7 @@ import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox; import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
import com.android.emailcommon.utility.Utility.ListStateSaver; import com.android.emailcommon.utility.Utility.ListStateSaver;
@ -42,13 +43,11 @@ import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@ -160,7 +159,7 @@ public class MessageListFragment extends ListFragment
private Utility.ListStateSaver mSavedListState; private Utility.ListStateSaver mSavedListState;
private MessageOpenTask mMessageOpenTask; private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
/** /**
* Callback interface that owning activities must implement * Callback interface that owning activities must implement
@ -306,8 +305,7 @@ public class MessageListFragment extends ListFragment
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageListFragment onDestroy"); Log.d(Logging.LOG_TAG, "MessageListFragment onDestroy");
} }
Utility.cancelTaskInterrupt(mMessageOpenTask); mTaskTracker.cancellAllInterrupt();
mMessageOpenTask = null;
mRefreshManager.unregisterListener(mRefreshListener); mRefreshManager.unregisterListener(mRefreshListener);
super.onDestroy(); super.onDestroy();
} }
@ -637,19 +635,18 @@ public class MessageListFragment extends ListFragment
* @param messageId ID of the msesage to open. * @param messageId ID of the msesage to open.
*/ */
private void onMessageOpen(final long messageMailboxId, final long messageId) { private void onMessageOpen(final long messageMailboxId, final long messageId) {
Utility.cancelTaskInterrupt(mMessageOpenTask); new MessageOpenTask(messageMailboxId, messageId).cancelPreviousAndExecuteParallel();
mMessageOpenTask = new MessageOpenTask(messageMailboxId, messageId);
mMessageOpenTask.execute();
} }
/** /**
* Task to look up the mailbox type for a message, and kicks the callback. * Task to look up the mailbox type for a message, and kicks the callback.
*/ */
private class MessageOpenTask extends AsyncTask<Void, Void, Integer> { private class MessageOpenTask extends EmailAsyncTask<Void, Void, Integer> {
private final long mMessageMailboxId; private final long mMessageMailboxId;
private final long mMessageId; private final long mMessageId;
public MessageOpenTask(long messageMailboxId, long messageId) { public MessageOpenTask(long messageMailboxId, long messageId) {
super(mTaskTracker);
mMessageMailboxId = messageMailboxId; mMessageMailboxId = messageMailboxId;
mMessageId = messageId; mMessageId = messageId;
} }

View File

@ -30,6 +30,7 @@ import com.android.emailcommon.Logging;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox; import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
import android.app.ActionBar; import android.app.ActionBar;
@ -39,7 +40,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.Loader; import android.content.Loader;
import android.database.Cursor; import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.TextUtils; import android.text.TextUtils;
@ -88,7 +88,7 @@ public class MessageListXL extends Activity implements
private final MessageOrderManagerCallback mMessageOrderManagerCallback private final MessageOrderManagerCallback mMessageOrderManagerCallback
= new MessageOrderManagerCallback(); = new MessageOrderManagerCallback();
private RefreshTask mRefreshTask; private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
private BannerController mBannerController; private BannerController mBannerController;
private TextView mErrorMessageView; private TextView mErrorMessageView;
@ -264,7 +264,7 @@ public class MessageListXL extends Activity implements
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, "MessageListXL onDestroy"); if (Email.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, "MessageListXL onDestroy");
mIsCreated = false; mIsCreated = false;
mController.removeResultCallback(mControllerResult); mController.removeResultCallback(mControllerResult);
Utility.cancelTaskInterrupt(mRefreshTask); mTaskTracker.cancellAllInterrupt();
mRefreshManager.unregisterListener(mMailRefreshManagerListener); mRefreshManager.unregisterListener(mMailRefreshManagerListener);
mFragmentManager.onDestroy(); mFragmentManager.onDestroy();
super.onDestroy(); super.onDestroy();
@ -561,7 +561,7 @@ public class MessageListXL extends Activity implements
* Call this when getting a connection error. * Call this when getting a connection error.
*/ */
private void showErrorMessage(final String rawMessage, final long accountId) { private void showErrorMessage(final String rawMessage, final long accountId) {
new AsyncTask<Void, Void, String>() { new EmailAsyncTask<Void, Void, String>(mTaskTracker) {
@Override @Override
protected String doInBackground(Void... params) { protected String doInBackground(Void... params) {
Account account = Account.restoreAccountWithId(MessageListXL.this, accountId); Account account = Account.restoreAccountWithId(MessageListXL.this, accountId);
@ -570,9 +570,6 @@ public class MessageListXL extends Activity implements
@Override @Override
protected void onPostExecute(String accountName) { protected void onPostExecute(String accountName) {
if (!mIsCreated) {
return; // activity destroyed.
}
final String message; final String message;
if (TextUtils.isEmpty(accountName)) { if (TextUtils.isEmpty(accountName)) {
message = rawMessage; message = rawMessage;
@ -585,8 +582,7 @@ public class MessageListXL extends Activity implements
mLastErrorAccountId = accountId; mLastErrorAccountId = accountId;
} }
} }
}.execute(); }.executeParallel();
} }
/** /**
@ -776,10 +772,8 @@ public class MessageListXL extends Activity implements
private void onRefresh() { private void onRefresh() {
// Cancel previously running instance if any. // Cancel previously running instance if any.
Utility.cancelTaskInterrupt(mRefreshTask); new RefreshTask(mTaskTracker, this, mFragmentManager.getActualAccountId(),
mRefreshTask = new RefreshTask(this, mFragmentManager.getActualAccountId(), mFragmentManager.getMailboxId()).cancelPreviousAndExecuteParallel();
mFragmentManager.getMailboxId());
mRefreshTask.execute();
} }
/** /**
@ -795,7 +789,7 @@ public class MessageListXL extends Activity implements
* {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. * {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}.
* </ul> * </ul>
*/ */
/* package */ static class RefreshTask extends AsyncTask<Void, Void, Boolean> { /* package */ static class RefreshTask extends EmailAsyncTask<Void, Void, Boolean> {
private final Clock mClock; private final Clock mClock;
private final Context mContext; private final Context mContext;
private final long mAccountId; private final long mAccountId;
@ -803,13 +797,15 @@ public class MessageListXL extends Activity implements
private final RefreshManager mRefreshManager; private final RefreshManager mRefreshManager;
/* package */ long mInboxId; /* package */ long mInboxId;
public RefreshTask(Context context, long accountId, long mailboxId) { public RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId,
this(context, accountId, mailboxId, Clock.INSTANCE, long mailboxId) {
this(tracker, context, accountId, mailboxId, Clock.INSTANCE,
RefreshManager.getInstance(context)); RefreshManager.getInstance(context));
} }
/* package */ RefreshTask(Context context, long accountId, long mailboxId, Clock clock, /* package */ RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId,
RefreshManager refreshManager) { long mailboxId, Clock clock, RefreshManager refreshManager) {
super(tracker);
mClock = clock; mClock = clock;
mContext = context; mContext = context;
mRefreshManager = refreshManager; mRefreshManager = refreshManager;

View File

@ -56,7 +56,6 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
@ -137,11 +136,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private long mMessageId = -1; private long mMessageId = -1;
private Message mMessage; private Message mMessage;
private LoadMessageTask mLoadMessageTask;
private ReloadMessageTask mReloadMessageTask;
private LoadBodyTask mLoadBodyTask;
private LoadAttachmentsTask mLoadAttachmentsTask;
private Controller mController; private Controller mController;
private ControllerResultUiThreadWrapper<ControllerResults> mControllerCallback; private ControllerResultUiThreadWrapper<ControllerResults> mControllerCallback;
@ -206,8 +200,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private boolean mRestoredPictureLoaded; private boolean mRestoredPictureLoaded;
private final EmailAsyncTask.Tracker mUpdatePreviewIconTaskTracker private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
= new EmailAsyncTask.Tracker();
/** /**
* Zoom scales for webview. Values correspond to {@link Preferences#TEXT_ZOOM_TINY}.. * Zoom scales for webview. Values correspond to {@link Preferences#TEXT_ZOOM_TINY}..
@ -427,15 +420,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private void cancelAllTasks() { private void cancelAllTasks() {
mMessageObserver.unregister(); mMessageObserver.unregister();
mUpdatePreviewIconTaskTracker.cancellAllInterrupt(); mTaskTracker.cancellAllInterrupt();
Utility.cancelTaskInterrupt(mLoadMessageTask);
mLoadMessageTask = null;
Utility.cancelTaskInterrupt(mReloadMessageTask);
mReloadMessageTask = null;
Utility.cancelTaskInterrupt(mLoadBodyTask);
mLoadBodyTask = null;
Utility.cancelTaskInterrupt(mLoadAttachmentsTask);
mLoadAttachmentsTask = null;
} }
/** /**
@ -483,8 +468,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mLoadWhenResumed = false; mLoadWhenResumed = false;
cancelAllTasks(); cancelAllTasks();
resetView(); resetView();
mLoadMessageTask = new LoadMessageTask(true); new LoadMessageTask(true).executeParallel();
mLoadMessageTask.execute();
} }
/** /**
@ -816,7 +800,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
// assume the download won't start right away, and we make the cancel button visible // assume the download won't start right away, and we make the cancel button visible
attachment.cancelButton.setVisibility(View.GONE); attachment.cancelButton.setVisibility(View.GONE);
// Create the timed task that will change the button state // Create the timed task that will change the button state
new AsyncTask<Void, Void, Void>() { new EmailAsyncTask<Void, Void, Void>(mTaskTracker) {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
try { try {
@ -831,7 +815,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
attachment.cancelButton.setVisibility(View.VISIBLE); attachment.cancelButton.setVisibility(View.VISIBLE);
} }
} }
}.execute(); }.executeParallel();
} else { } else {
attachment.cancelButton.setVisibility(View.VISIBLE); attachment.cancelButton.setVisibility(View.VISIBLE);
} }
@ -966,7 +950,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
/** /**
* Async task for loading a single message outside of the UI thread * Async task for loading a single message outside of the UI thread
*/ */
private class LoadMessageTask extends AsyncTask<Void, Void, Message> { private class LoadMessageTask extends EmailAsyncTask<Void, Void, Message> {
private final boolean mOkToFetch; private final boolean mOkToFetch;
private int mMailboxType; private int mMailboxType;
@ -975,6 +959,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
* Special constructor to cache some local info * Special constructor to cache some local info
*/ */
public LoadMessageTask(boolean okToFetch) { public LoadMessageTask(boolean okToFetch) {
super(mTaskTracker);
mOkToFetch = okToFetch; mOkToFetch = okToFetch;
} }
@ -1015,7 +1000,11 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
/** /**
* Kicked by {@link MessageObserver}. Reload the message and update the views. * Kicked by {@link MessageObserver}. Reload the message and update the views.
*/ */
private class ReloadMessageTask extends AsyncTask<Void, Void, Message> { private class ReloadMessageTask extends EmailAsyncTask<Void, Void, Message> {
public ReloadMessageTask() {
super(mTaskTracker);
}
@Override @Override
protected Message doInBackground(Void... params) { protected Message doInBackground(Void... params) {
if (!isMessageSpecified()) { // just in case if (!isMessageSpecified()) { // just in case
@ -1060,7 +1049,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
/** /**
* Async task for loading a single message body outside of the UI thread * Async task for loading a single message body outside of the UI thread
*/ */
private class LoadBodyTask extends AsyncTask<Void, Void, String[]> { private class LoadBodyTask extends EmailAsyncTask<Void, Void, String[]> {
private long mId; private long mId;
private boolean mErrorLoadingMessageBody; private boolean mErrorLoadingMessageBody;
@ -1069,6 +1058,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
* Special constructor to cache some local info * Special constructor to cache some local info
*/ */
public LoadBodyTask(long messageId) { public LoadBodyTask(long messageId) {
super(mTaskTracker);
mId = messageId; mId = messageId;
} }
@ -1112,7 +1102,11 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
* this implementation is incomplete, as it will fail to refresh properly if the message is * this implementation is incomplete, as it will fail to refresh properly if the message is
* partially loaded at this time. * partially loaded at this time.
*/ */
private class LoadAttachmentsTask extends AsyncTask<Long, Void, Attachment[]> { private class LoadAttachmentsTask extends EmailAsyncTask<Long, Void, Attachment[]> {
public LoadAttachmentsTask() {
super(mTaskTracker);
}
@Override @Override
protected Attachment[] doInBackground(Long... messageIds) { protected Attachment[] doInBackground(Long... messageIds) {
return Attachment.restoreAttachmentsWithMessageId(mContext, messageIds[0]); return Attachment.restoreAttachmentsWithMessageId(mContext, messageIds[0]);
@ -1419,8 +1413,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
} else { } else {
mControllerCallback.getWrappee().setWaitForLoadMessageId(-1); mControllerCallback.getWrappee().setWaitForLoadMessageId(-1);
// Ask for body // Ask for body
mLoadBodyTask = new LoadBodyTask(message.mId); new LoadBodyTask(message.mId).executeParallel();
mLoadBodyTask.execute();
} }
} }
@ -1560,8 +1553,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
setMessageHtml(text); setMessageHtml(text);
// Ask for attachments after body // Ask for attachments after body
mLoadAttachmentsTask = new LoadAttachmentsTask(); new LoadAttachmentsTask().executeParallel(mMessage.mId);
mLoadAttachmentsTask.execute(mMessage.mId);
mIsMessageLoadedForTest = true; mIsMessageLoadedForTest = true;
} }
@ -1626,8 +1618,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
// reload UI and reload everything else too // reload UI and reload everything else too
// pass false to LoadMessageTask to prevent looping here // pass false to LoadMessageTask to prevent looping here
cancelAllTasks(); cancelAllTasks();
mLoadMessageTask = new LoadMessageTask(false); new LoadMessageTask(false).executeParallel();
mLoadMessageTask.execute();
break; break;
default: default:
// do nothing - we don't have a progress bar at this time // do nothing - we don't have a progress bar at this time
@ -1748,14 +1739,12 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
if (!isMessageSpecified()) { if (!isMessageSpecified()) {
return; return;
} }
Utility.cancelTaskInterrupt(mReloadMessageTask); new ReloadMessageTask().cancelPreviousAndExecuteParallel();
mReloadMessageTask = new ReloadMessageTask();
mReloadMessageTask.execute();
} }
} }
private void updatePreviewIcon(MessageViewAttachmentInfo attachmentInfo) { private void updatePreviewIcon(MessageViewAttachmentInfo attachmentInfo) {
new UpdatePreviewIconTask(attachmentInfo).execute(); new UpdatePreviewIconTask(attachmentInfo).executeParallel();
} }
private class UpdatePreviewIconTask extends EmailAsyncTask<Void, Void, Bitmap> { private class UpdatePreviewIconTask extends EmailAsyncTask<Void, Void, Bitmap> {
@ -1764,7 +1753,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private final MessageViewAttachmentInfo mAttachmentInfo; private final MessageViewAttachmentInfo mAttachmentInfo;
public UpdatePreviewIconTask(MessageViewAttachmentInfo attachmentInfo) { public UpdatePreviewIconTask(MessageViewAttachmentInfo attachmentInfo) {
super(mUpdatePreviewIconTaskTracker); super(mTaskTracker);
mContext = getActivity(); mContext = getActivity();
mAttachmentInfo = attachmentInfo; mAttachmentInfo = attachmentInfo;
} }

View File

@ -57,8 +57,8 @@ public class MessageListXLRefreshTaskTest extends AndroidTestCase {
final long ACCOUNT_ID = 5; final long ACCOUNT_ID = 5;
final long MAILBOX_ID = 10; final long MAILBOX_ID = 10;
MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(getContext(), ACCOUNT_ID, MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(null, getContext(),
MAILBOX_ID, mClock, mRefreshManager); ACCOUNT_ID, MAILBOX_ID, mClock, mRefreshManager);
mRefreshManager.mExpectedAccountId = ACCOUNT_ID; mRefreshManager.mExpectedAccountId = ACCOUNT_ID;
@ -100,8 +100,8 @@ public class MessageListXLRefreshTaskTest extends AndroidTestCase {
final long ACCOUNT_ID = 5; final long ACCOUNT_ID = 5;
final long MAILBOX_ID = 10; final long MAILBOX_ID = 10;
MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(getContext(), ACCOUNT_ID, MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(null, getContext(),
MAILBOX_ID, mClock, mRefreshManager); ACCOUNT_ID, MAILBOX_ID, mClock, mRefreshManager);
mRefreshManager.mExpectedAccountId = ACCOUNT_ID; mRefreshManager.mExpectedAccountId = ACCOUNT_ID;

View File

@ -16,6 +16,8 @@
package com.android.emailcommon.utility; package com.android.emailcommon.utility;
import com.android.emailcommon.utility.EmailAsyncTask.Tracker;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.MoreAsserts; import android.test.MoreAsserts;
@ -82,6 +84,37 @@ public class EmailAsyncTaskTests extends AndroidTestCase {
task1.unregisterSelf(); task1.unregisterSelf();
} }
/**
* Test for {@link EmailAsyncTask.Tracker#cancelOthers}
*/
public void testCancellOthers() {
final EmailAsyncTask.Tracker tracker = new EmailAsyncTask.Tracker();
final MyTask task1 = new MyTask(tracker);
final MyTask task2 = new MyTask(tracker);
final MyTask task3 = new MyTask(tracker);
final MyTask sub1 = new MyTaskSubClass(tracker);
final MyTask sub2 = new MyTaskSubClass(tracker);
final MyTask sub3 = new MyTaskSubClass(tracker);
// All should be in the tracker.
assertEquals(6, tracker.getTaskCountForTest());
// This should remove task1, task2, but not task3 itself.
tracker.cancelOthers(task3);
assertEquals(4, tracker.getTaskCountForTest());
assertTrue(tracker.containsTaskForTest(task3));
// Same for sub1.
tracker.cancelOthers(sub1);
assertEquals(2, tracker.getTaskCountForTest());
assertTrue(tracker.containsTaskForTest(task3));
assertTrue(tracker.containsTaskForTest(sub1));
}
private static class MyTask extends EmailAsyncTask<String, String, String> { private static class MyTask extends EmailAsyncTask<String, String, String> {
public String[] mDoInBackgroundArg; public String[] mDoInBackgroundArg;
public String mDoInBackgroundResult; public String mDoInBackgroundResult;
@ -108,4 +141,10 @@ public class EmailAsyncTaskTests extends AndroidTestCase {
mOnPostExecuteArg = result; mOnPostExecuteArg = result;
} }
} }
private static class MyTaskSubClass extends MyTask {
public MyTaskSubClass(Tracker tracker) {
super(tracker);
}
}
} }