From d72f7bdf114a21db6aac66a7e83d6b002c8e8ed5 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 21 Mar 2011 14:08:57 -0700 Subject: [PATCH] Improve EmailAsyncTask Added 6 methods: - execute{Parallel,Serial} - cancelPreviousAndExecute{Parallel,Serial} - runAsyncParallel{Parallel,Serial} (replacement for Utility.runAsync) Bug 4083415 Change-Id: I5ca33000e52fc5265ccc84a6e5acb0d3359d0eb4 --- .../emailcommon/utility/EmailAsyncTask.java | 129 ++++++++++++++++-- .../android/emailcommon/utility/Utility.java | 6 +- .../email/activity/AccountShortcutPicker.java | 16 +-- .../email/activity/MessageCompose.java | 40 +++--- .../email/activity/MessageListFragment.java | 15 +- .../android/email/activity/MessageListXL.java | 32 ++--- .../activity/MessageViewFragmentBase.java | 61 ++++----- .../MessageListXLRefreshTaskTest.java | 8 +- .../utility/EmailAsyncTaskTests.java | 39 ++++++ 9 files changed, 237 insertions(+), 109 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/utility/EmailAsyncTask.java b/emailcommon/src/com/android/emailcommon/utility/EmailAsyncTask.java index c157e2ff3..9aaa9aeb2 100644 --- a/emailcommon/src/com/android/emailcommon/utility/EmailAsyncTask.java +++ b/emailcommon/src/com/android/emailcommon/utility/EmailAsyncTask.java @@ -18,14 +18,16 @@ package com.android.emailcommon.utility; import android.os.AsyncTask; +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 cancell pending tasks + * - Bulk cancellation of multiple tasks. This is mainly used by UI to cancel pending tasks * in onDestroy() or similar places. * - More features to come... * @@ -33,6 +35,9 @@ import java.util.concurrent.ExecutionException; * {@link AsyncTask#onProgressUpdate}. Add these when necessary. */ public abstract class EmailAsyncTask { + private static final Executor SERIAL_EXECUTOR = AsyncTask.SERIAL_EXECUTOR; + private static final Executor PARALLEL_EXECUTOR = AsyncTask.THREAD_POOL_EXECUTOR; + /** * Tracks {@link EmailAsyncTask}. * @@ -66,9 +71,34 @@ public abstract class EmailAsyncTask { } } + /** + * 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> toRemove = + new ArrayList>(); + 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; @@ -109,46 +139,127 @@ public abstract class EmailAsyncTask { mInnerTask = new InnerTask(this); } - /* package */ void unregisterSelf() { + /* 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 boolean cancel(boolean mayInterruptIfRunning) { return mInnerTask.cancel(mayInterruptIfRunning); } + /** @see AsyncTask#onCancelled */ protected void onCancelled(Result result) { } + /** @see AsyncTask#onPostExecute */ protected void onPostExecute(Result result) { } - public final EmailAsyncTask execute(Params... params) { - mInnerTask.execute(params); + /** + * execute on {@link #PARALLEL_EXECUTOR} + * + * @see AsyncTask#execute + */ + public final EmailAsyncTask executeParallel(Params... params) { + return executeInternal(PARALLEL_EXECUTOR, false, params); + } + + /** + * execute on {@link #SERIAL_EXECUTOR} + * + * @see AsyncTask#execute + */ + public final EmailAsyncTask 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 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 cancelPreviousAndExecuteSerial( + Params... params) { + return executeInternal(SERIAL_EXECUTOR, true, params); + } + + private final EmailAsyncTask 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; } - 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 runAsyncParallel(Runnable runnable) { + return runAsyncInternal(PARALLEL_EXECUTOR, runnable); } + /** + * Runs a {@link Runnable} in a bg thread, using {@link #SERIAL_EXECUTOR}. + */ + public static EmailAsyncTask runAsyncSerial(Runnable runnable) { + return runAsyncInternal(SERIAL_EXECUTOR, runnable); + } + + private static EmailAsyncTask runAsyncInternal(Executor executor, + final Runnable runnable) { + EmailAsyncTask task = new EmailAsyncTask(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() { return mInnerTask.isCancelled(); } - /* package */ Result callDoInBackgroundForTest(Params... params) { + /* package */ final Result callDoInBackgroundForTest(Params... params) { return mInnerTask.doInBackground(params); } - /* package */ void callOnCancelledForTest(Result result) { + /* package */ final void callOnCancelledForTest(Result result) { mInnerTask.onCancelled(result); } - /* package */ void callOnPostExecuteForTest(Result result) { + /* package */ final void callOnPostExecuteForTest(Result result) { mInnerTask.onPostExecute(result); } } diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java index f89b6082f..ce2b0bb0a 100644 --- a/emailcommon/src/com/android/emailcommon/utility/Utility.java +++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java @@ -573,14 +573,18 @@ public class Utility { * Run {@code r} on a worker thread, returning the AsyncTask * @return the AsyncTask; this is primarily for use by unit tests, which require the * result of the task + * + * @deprecated use {@link EmailAsyncTask#runAsyncParallel} or + * {@link EmailAsyncTask#runAsyncSerial} */ + @Deprecated public static AsyncTask runAsync(final Runnable r) { return new AsyncTask() { @Override protected Void doInBackground(Void... params) { r.run(); return null; } - }.execute(); + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } /** diff --git a/src/com/android/email/activity/AccountShortcutPicker.java b/src/com/android/email/activity/AccountShortcutPicker.java index 2e422d8ce..0145fc91d 100644 --- a/src/com/android/email/activity/AccountShortcutPicker.java +++ b/src/com/android/email/activity/AccountShortcutPicker.java @@ -19,12 +19,11 @@ package com.android.email.activity; import com.android.email.R; import com.android.emailcommon.provider.EmailContent; 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.content.Intent; import android.database.Cursor; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.view.View; @@ -43,7 +42,7 @@ import android.widget.SimpleCursorAdapter; public class AccountShortcutPicker extends ListActivity implements OnClickListener, OnItemClickListener { - private AccountTask mAccountTask; + private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); /** * Support for list adapter @@ -74,16 +73,14 @@ public class AccountShortcutPicker extends ListActivity listView.setOnItemClickListener(this); listView.setItemsCanFocus(false); - mAccountTask = new AccountTask(); - mAccountTask.execute(); + new AccountTask().executeParallel(); } @Override public void onDestroy() { super.onDestroy(); // Cleanup running async task (if any) - Utility.cancelTaskInterrupt(mAccountTask); - mAccountTask = null; + mTaskTracker.cancellAllInterrupt(); // Cleanup accounts cursor (if any) SimpleCursorAdapter adapter = (SimpleCursorAdapter) getListAdapter(); if (adapter != null) { @@ -114,7 +111,10 @@ public class AccountShortcutPicker extends ListActivity /** * Load the accounts and create the adapter. */ - private class AccountTask extends AsyncTask { + private class AccountTask extends EmailAsyncTask { + public AccountTask() { + super(mTaskTracker); + } @Override protected Cursor doInBackground(Void... params) { diff --git a/src/com/android/email/activity/MessageCompose.java b/src/com/android/email/activity/MessageCompose.java index 4bd13ebec..09ce6e62d 100644 --- a/src/com/android/email/activity/MessageCompose.java +++ b/src/com/android/email/activity/MessageCompose.java @@ -34,6 +34,7 @@ import com.android.emailcommon.provider.EmailContent.BodyColumns; import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.MessageColumns; import com.android.emailcommon.utility.AttachmentUtilities; +import com.android.emailcommon.utility.EmailAsyncTask; import com.android.emailcommon.utility.Utility; import android.app.ActionBar; @@ -48,7 +49,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.database.Cursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.OpenableColumns; @@ -157,8 +157,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus private Controller mController; private boolean mDraftNeedsSaving; private boolean mMessageLoaded; - private AsyncTask mLoadAttachmentsTask; - private AsyncTask mLoadMessageTask; + private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); private EmailAddressAdapter mAddressAdapterTo; 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) long messageId = draftId != -1 ? draftId : intent.getLongExtra(EXTRA_MESSAGE_ID, -1); if (messageId != -1) { - mLoadMessageTask = new LoadMessageTask(messageId).execute(); + new LoadMessageTask(messageId).executeParallel(); } else { setAccount(intent); // 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 = null; - Utility.cancelTaskInterrupt(mLoadAttachmentsTask); - mLoadAttachmentsTask = null; - Utility.cancelTaskInterrupt(mLoadMessageTask); - mLoadMessageTask = null; + mTaskTracker.cancellAllInterrupt(); if (mAddressAdapterTo != null) { mAddressAdapterTo.close(); @@ -608,10 +604,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus mAddressAdapterBcc = new EmailAddressAdapter(this); } - private class LoadMessageTask extends AsyncTask { + private class LoadMessageTask extends EmailAsyncTask { private final long mMessageId; public LoadMessageTask(long messageId) { + super(mTaskTracker); mMessageId = messageId; } @@ -686,7 +683,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus } else { mSource = message; } - mLoadAttachmentsTask = new AsyncTask() { + new EmailAsyncTask(mTaskTracker) { @Override protected Attachment[] doInBackground(Long... messageIds) { return Attachment.restoreAttachmentsWithMessageId(MessageCompose.this, @@ -716,7 +713,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus addAttachment(attachment, allowDelete); } } - }.execute(message.mId); + }.executeParallel(message.mId); } else if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction)) { mSource = message; } else if (Email.LOGD) { @@ -909,10 +906,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus } } - private class SendOrSaveMessageTask extends AsyncTask { + private class SendOrSaveMessageTask extends EmailAsyncTask { private final boolean mSend; public SendOrSaveMessageTask(boolean send) { + super(null /* DO NOT cancel in onDestroy */); if (send && ActivityManager.isUserAMonkey()) { Log.d(Logging.LOG_TAG, "Inhibiting send while monkey is in charge."); send = false; @@ -994,9 +992,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus sSaveInProgress = false; sSaveInProgressCondition.notify(); } - if (isCancelled()) { - return; - } // Don't display the toast if the user is just changing the orientation if (!mSend && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) { Toast.makeText(MessageCompose.this, R.string.message_saved_toast, @@ -1020,7 +1015,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus synchronized (sSaveInProgressCondition) { sSaveInProgress = true; } - new SendOrSaveMessageTask(send).execute(); + new SendOrSaveMessageTask(send).executeParallel(); } private void saveIfNeeded() { @@ -1252,16 +1247,13 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus mAttachments.removeView(attachmentView); updateAttachmentContainer(); if (attachment.mMessageKey == mDraft.mId && attachment.isSaved()) { - // The following async task for deleting attachments: - // - can be started multiple times in parallel (to delete multiple attachments). - // - need not be interrupted on activity exit, instead should run to completion. - new AsyncTask() { + final long attachmentId = attachment.mId; + EmailAsyncTask.runAsyncParallel(new Runnable() { @Override - protected Void doInBackground(Long... attachmentIds) { - mController.deleteAttachment(attachmentIds[0]); - return null; + public void run() { + mController.deleteAttachment(attachmentId); } - }.execute(attachment.mId); + }); } setDraftNeedsSaving(true); } diff --git a/src/com/android/email/activity/MessageListFragment.java b/src/com/android/email/activity/MessageListFragment.java index e9d1cd4e4..fe5c935da 100644 --- a/src/com/android/email/activity/MessageListFragment.java +++ b/src/com/android/email/activity/MessageListFragment.java @@ -28,6 +28,7 @@ import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.Mailbox; 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.ListStateSaver; @@ -42,13 +43,11 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -160,7 +159,7 @@ public class MessageListFragment extends ListFragment private Utility.ListStateSaver mSavedListState; - private MessageOpenTask mMessageOpenTask; + private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); /** * Callback interface that owning activities must implement @@ -306,8 +305,7 @@ public class MessageListFragment extends ListFragment if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "MessageListFragment onDestroy"); } - Utility.cancelTaskInterrupt(mMessageOpenTask); - mMessageOpenTask = null; + mTaskTracker.cancellAllInterrupt(); mRefreshManager.unregisterListener(mRefreshListener); super.onDestroy(); } @@ -637,19 +635,18 @@ public class MessageListFragment extends ListFragment * @param messageId ID of the msesage to open. */ private void onMessageOpen(final long messageMailboxId, final long messageId) { - Utility.cancelTaskInterrupt(mMessageOpenTask); - mMessageOpenTask = new MessageOpenTask(messageMailboxId, messageId); - mMessageOpenTask.execute(); + new MessageOpenTask(messageMailboxId, messageId).cancelPreviousAndExecuteParallel(); } /** * Task to look up the mailbox type for a message, and kicks the callback. */ - private class MessageOpenTask extends AsyncTask { + private class MessageOpenTask extends EmailAsyncTask { private final long mMessageMailboxId; private final long mMessageId; public MessageOpenTask(long messageMailboxId, long messageId) { + super(mTaskTracker); mMessageMailboxId = messageMailboxId; mMessageId = messageId; } diff --git a/src/com/android/email/activity/MessageListXL.java b/src/com/android/email/activity/MessageListXL.java index fda472aea..45b60fd6f 100644 --- a/src/com/android/email/activity/MessageListXL.java +++ b/src/com/android/email/activity/MessageListXL.java @@ -30,6 +30,7 @@ import com.android.emailcommon.Logging; import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.EmailContent.Mailbox; +import com.android.emailcommon.utility.EmailAsyncTask; import com.android.emailcommon.utility.Utility; import android.app.ActionBar; @@ -39,7 +40,6 @@ import android.content.Context; import android.content.Intent; import android.content.Loader; import android.database.Cursor; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.text.TextUtils; @@ -88,7 +88,7 @@ public class MessageListXL extends Activity implements private final MessageOrderManagerCallback mMessageOrderManagerCallback = new MessageOrderManagerCallback(); - private RefreshTask mRefreshTask; + private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); private BannerController mBannerController; 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"); mIsCreated = false; mController.removeResultCallback(mControllerResult); - Utility.cancelTaskInterrupt(mRefreshTask); + mTaskTracker.cancellAllInterrupt(); mRefreshManager.unregisterListener(mMailRefreshManagerListener); mFragmentManager.onDestroy(); super.onDestroy(); @@ -561,7 +561,7 @@ public class MessageListXL extends Activity implements * Call this when getting a connection error. */ private void showErrorMessage(final String rawMessage, final long accountId) { - new AsyncTask() { + new EmailAsyncTask(mTaskTracker) { @Override protected String doInBackground(Void... params) { Account account = Account.restoreAccountWithId(MessageListXL.this, accountId); @@ -570,9 +570,6 @@ public class MessageListXL extends Activity implements @Override protected void onPostExecute(String accountName) { - if (!mIsCreated) { - return; // activity destroyed. - } final String message; if (TextUtils.isEmpty(accountName)) { message = rawMessage; @@ -585,8 +582,7 @@ public class MessageListXL extends Activity implements mLastErrorAccountId = accountId; } } - }.execute(); - + }.executeParallel(); } /** @@ -776,10 +772,8 @@ public class MessageListXL extends Activity implements private void onRefresh() { // Cancel previously running instance if any. - Utility.cancelTaskInterrupt(mRefreshTask); - mRefreshTask = new RefreshTask(this, mFragmentManager.getActualAccountId(), - mFragmentManager.getMailboxId()); - mRefreshTask.execute(); + new RefreshTask(mTaskTracker, this, mFragmentManager.getActualAccountId(), + mFragmentManager.getMailboxId()).cancelPreviousAndExecuteParallel(); } /** @@ -795,7 +789,7 @@ public class MessageListXL extends Activity implements * {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. * */ - /* package */ static class RefreshTask extends AsyncTask { + /* package */ static class RefreshTask extends EmailAsyncTask { private final Clock mClock; private final Context mContext; private final long mAccountId; @@ -803,13 +797,15 @@ public class MessageListXL extends Activity implements private final RefreshManager mRefreshManager; /* package */ long mInboxId; - public RefreshTask(Context context, long accountId, long mailboxId) { - this(context, accountId, mailboxId, Clock.INSTANCE, + public RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, + long mailboxId) { + this(tracker, context, accountId, mailboxId, Clock.INSTANCE, RefreshManager.getInstance(context)); } - /* package */ RefreshTask(Context context, long accountId, long mailboxId, Clock clock, - RefreshManager refreshManager) { + /* package */ RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, + long mailboxId, Clock clock, RefreshManager refreshManager) { + super(tracker); mClock = clock; mContext = context; mRefreshManager = refreshManager; diff --git a/src/com/android/email/activity/MessageViewFragmentBase.java b/src/com/android/email/activity/MessageViewFragmentBase.java index ce06f2c2c..a6c11ba46 100644 --- a/src/com/android/email/activity/MessageViewFragmentBase.java +++ b/src/com/android/email/activity/MessageViewFragmentBase.java @@ -56,7 +56,6 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaScannerConnection; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -137,11 +136,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O private long mMessageId = -1; private Message mMessage; - private LoadMessageTask mLoadMessageTask; - private ReloadMessageTask mReloadMessageTask; - private LoadBodyTask mLoadBodyTask; - private LoadAttachmentsTask mLoadAttachmentsTask; - private Controller mController; private ControllerResultUiThreadWrapper mControllerCallback; @@ -206,8 +200,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O private boolean mRestoredPictureLoaded; - private final EmailAsyncTask.Tracker mUpdatePreviewIconTaskTracker - = new EmailAsyncTask.Tracker(); + private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); /** * 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() { mMessageObserver.unregister(); - mUpdatePreviewIconTaskTracker.cancellAllInterrupt(); - Utility.cancelTaskInterrupt(mLoadMessageTask); - mLoadMessageTask = null; - Utility.cancelTaskInterrupt(mReloadMessageTask); - mReloadMessageTask = null; - Utility.cancelTaskInterrupt(mLoadBodyTask); - mLoadBodyTask = null; - Utility.cancelTaskInterrupt(mLoadAttachmentsTask); - mLoadAttachmentsTask = null; + mTaskTracker.cancellAllInterrupt(); } /** @@ -483,8 +468,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O mLoadWhenResumed = false; cancelAllTasks(); resetView(); - mLoadMessageTask = new LoadMessageTask(true); - mLoadMessageTask.execute(); + new LoadMessageTask(true).executeParallel(); } /** @@ -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 attachment.cancelButton.setVisibility(View.GONE); // Create the timed task that will change the button state - new AsyncTask() { + new EmailAsyncTask(mTaskTracker) { @Override protected Void doInBackground(Void... params) { try { @@ -831,7 +815,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O attachment.cancelButton.setVisibility(View.VISIBLE); } } - }.execute(); + }.executeParallel(); } else { 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 */ - private class LoadMessageTask extends AsyncTask { + private class LoadMessageTask extends EmailAsyncTask { private final boolean mOkToFetch; private int mMailboxType; @@ -975,6 +959,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O * Special constructor to cache some local info */ public LoadMessageTask(boolean okToFetch) { + super(mTaskTracker); 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. */ - private class ReloadMessageTask extends AsyncTask { + private class ReloadMessageTask extends EmailAsyncTask { + public ReloadMessageTask() { + super(mTaskTracker); + } + @Override protected Message doInBackground(Void... params) { 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 */ - private class LoadBodyTask extends AsyncTask { + private class LoadBodyTask extends EmailAsyncTask { private long mId; private boolean mErrorLoadingMessageBody; @@ -1069,6 +1058,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O * Special constructor to cache some local info */ public LoadBodyTask(long messageId) { + super(mTaskTracker); 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 * partially loaded at this time. */ - private class LoadAttachmentsTask extends AsyncTask { + private class LoadAttachmentsTask extends EmailAsyncTask { + public LoadAttachmentsTask() { + super(mTaskTracker); + } + @Override protected Attachment[] doInBackground(Long... messageIds) { return Attachment.restoreAttachmentsWithMessageId(mContext, messageIds[0]); @@ -1419,8 +1413,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O } else { mControllerCallback.getWrappee().setWaitForLoadMessageId(-1); // Ask for body - mLoadBodyTask = new LoadBodyTask(message.mId); - mLoadBodyTask.execute(); + new LoadBodyTask(message.mId).executeParallel(); } } @@ -1560,8 +1553,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O setMessageHtml(text); // Ask for attachments after body - mLoadAttachmentsTask = new LoadAttachmentsTask(); - mLoadAttachmentsTask.execute(mMessage.mId); + new LoadAttachmentsTask().executeParallel(mMessage.mId); mIsMessageLoadedForTest = true; } @@ -1626,8 +1618,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O // reload UI and reload everything else too // pass false to LoadMessageTask to prevent looping here cancelAllTasks(); - mLoadMessageTask = new LoadMessageTask(false); - mLoadMessageTask.execute(); + new LoadMessageTask(false).executeParallel(); break; default: // 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()) { return; } - Utility.cancelTaskInterrupt(mReloadMessageTask); - mReloadMessageTask = new ReloadMessageTask(); - mReloadMessageTask.execute(); + new ReloadMessageTask().cancelPreviousAndExecuteParallel(); } } private void updatePreviewIcon(MessageViewAttachmentInfo attachmentInfo) { - new UpdatePreviewIconTask(attachmentInfo).execute(); + new UpdatePreviewIconTask(attachmentInfo).executeParallel(); } private class UpdatePreviewIconTask extends EmailAsyncTask { @@ -1764,7 +1753,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O private final MessageViewAttachmentInfo mAttachmentInfo; public UpdatePreviewIconTask(MessageViewAttachmentInfo attachmentInfo) { - super(mUpdatePreviewIconTaskTracker); + super(mTaskTracker); mContext = getActivity(); mAttachmentInfo = attachmentInfo; } diff --git a/tests/src/com/android/email/activity/MessageListXLRefreshTaskTest.java b/tests/src/com/android/email/activity/MessageListXLRefreshTaskTest.java index 8a0ff606d..ec48d453a 100644 --- a/tests/src/com/android/email/activity/MessageListXLRefreshTaskTest.java +++ b/tests/src/com/android/email/activity/MessageListXLRefreshTaskTest.java @@ -57,8 +57,8 @@ public class MessageListXLRefreshTaskTest extends AndroidTestCase { final long ACCOUNT_ID = 5; final long MAILBOX_ID = 10; - MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(getContext(), ACCOUNT_ID, - MAILBOX_ID, mClock, mRefreshManager); + MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(null, getContext(), + ACCOUNT_ID, MAILBOX_ID, mClock, mRefreshManager); mRefreshManager.mExpectedAccountId = ACCOUNT_ID; @@ -100,8 +100,8 @@ public class MessageListXLRefreshTaskTest extends AndroidTestCase { final long ACCOUNT_ID = 5; final long MAILBOX_ID = 10; - MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(getContext(), ACCOUNT_ID, - MAILBOX_ID, mClock, mRefreshManager); + MessageListXL.RefreshTask task = new MessageListXL.RefreshTask(null, getContext(), + ACCOUNT_ID, MAILBOX_ID, mClock, mRefreshManager); mRefreshManager.mExpectedAccountId = ACCOUNT_ID; diff --git a/tests/src/com/android/emailcommon/utility/EmailAsyncTaskTests.java b/tests/src/com/android/emailcommon/utility/EmailAsyncTaskTests.java index b0315cfa4..bdebf2ebe 100644 --- a/tests/src/com/android/emailcommon/utility/EmailAsyncTaskTests.java +++ b/tests/src/com/android/emailcommon/utility/EmailAsyncTaskTests.java @@ -16,6 +16,8 @@ package com.android.emailcommon.utility; +import com.android.emailcommon.utility.EmailAsyncTask.Tracker; + import android.test.AndroidTestCase; import android.test.MoreAsserts; @@ -82,6 +84,37 @@ public class EmailAsyncTaskTests extends AndroidTestCase { 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 { public String[] mDoInBackgroundArg; public String mDoInBackgroundResult; @@ -108,4 +141,10 @@ public class EmailAsyncTaskTests extends AndroidTestCase { mOnPostExecuteArg = result; } } + + private static class MyTaskSubClass extends MyTask { + public MyTaskSubClass(Tracker tracker) { + super(tracker); + } + } }