Clear references to inner classes in onDestroy().

Also
- Made inner classes private if possible.
- Made some members final.

Bug 2570603

Change-Id: I34451000f2540c67e1039ea9dc4839dbec5ffab7
This commit is contained in:
Makoto Onuki 2010-04-06 10:25:15 -07:00
parent c94c077a66
commit 59cf1d05c1
8 changed files with 110 additions and 101 deletions

View File

@ -31,6 +31,7 @@ import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.text.Editable; import android.text.Editable;
import android.util.Base64; import android.util.Base64;
import android.widget.TextView; import android.widget.TextView;
@ -506,4 +507,24 @@ public class Utility {
public static String replaceBareLfWithCrlf(String str) { public static String replaceBareLfWithCrlf(String str) {
return str.replace("\r", "").replace("\n", "\r\n"); return str.replace("\r", "").replace("\n", "\r\n");
} }
/**
* Cancel an {@link AsyncTask}. If it's already running, it'll be interrupted.
*/
public static void cancelTaskInterrupt(AsyncTask<?, ?, ?> task) {
cancelTask(task, true);
}
/**
* Cancel an {@link AsyncTask}.
*
* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete.
*/
public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) {
if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
task.cancel(mayInterruptIfRunning);
}
}
} }

View File

@ -91,9 +91,8 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
private LoadAccountsTask mLoadAccountsTask; private LoadAccountsTask mLoadAccountsTask;
private DeleteAccountTask mDeleteAccountTask; private DeleteAccountTask mDeleteAccountTask;
private MessageListHandler mHandler;
private MessageListHandler mHandler = new MessageListHandler(); private ControllerResults mControllerCallback;
private ControllerResults mControllerCallback = new ControllerResults();
/** /**
* Reduced mailbox projection used by AccountsAdapter * Reduced mailbox projection used by AccountsAdapter
@ -154,6 +153,8 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
R.layout.list_title); R.layout.list_title);
mHandler = new MessageListHandler();
mControllerCallback = new ControllerResults();
mProgressIcon = (ProgressBar) findViewById(R.id.title_progress_icon); mProgressIcon = (ProgressBar) findViewById(R.id.title_progress_icon);
mListView = getListView(); mListView = getListView();
@ -203,19 +204,21 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
Utility.cancelTaskInterrupt(mLoadAccountsTask);
mLoadAccountsTask = null;
if (mLoadAccountsTask != null && // TODO: We shouldn't call cancel() for DeleteAccountTask. If the task hasn't
mLoadAccountsTask.getStatus() != LoadAccountsTask.Status.FINISHED) { // started, this will mark it as "don't run", but we always want it to finish.
mLoadAccountsTask.cancel(true); // (But don't just remove this cancel() call. DeleteAccountTask.onPostExecute() checks if
mLoadAccountsTask = null; // it's been canceled to decided whether to update the UI.)
} Utility.cancelTask(mDeleteAccountTask, false); // Don't interrupt if it's running.
if (mDeleteAccountTask != null && mDeleteAccountTask = null;
mDeleteAccountTask.getStatus() != DeleteAccountTask.Status.FINISHED) {
mDeleteAccountTask.cancel(false); // false == allow the cancel to run to completion
mDeleteAccountTask = null;
}
mListAdapter.changeCursor(null); mListAdapter.changeCursor(null);
mListAdapter = null;
mHandler = null;
mControllerCallback = null;
} }
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@ -357,8 +360,8 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
} }
private class DeleteAccountTask extends AsyncTask<Void, Void, Void> { private class DeleteAccountTask extends AsyncTask<Void, Void, Void> {
private long mAccountId; private final long mAccountId;
private String mAccountUri; private final String mAccountUri;
public DeleteAccountTask(long accountId, String accountUri) { public DeleteAccountTask(long accountId, String accountUri) {
mAccountId = accountId; mAccountId = accountId;
@ -395,10 +398,7 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
} }
private void updateAccounts() { private void updateAccounts() {
if (mLoadAccountsTask != null && Utility.cancelTaskInterrupt(mLoadAccountsTask);
mLoadAccountsTask.getStatus() != LoadAccountsTask.Status.FINISHED) {
mLoadAccountsTask.cancel(true);
}
mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask().execute(); mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask().execute();
} }
@ -468,6 +468,7 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
int numAccounts = EmailContent.count(AccountFolderList.this, int numAccounts = EmailContent.count(AccountFolderList.this,
Account.CONTENT_URI, null, null); Account.CONTENT_URI, null, null);
mListAdapter.addOnDeletingAccount(mSelectedContextAccount.mId); mListAdapter.addOnDeletingAccount(mSelectedContextAccount.mId);
mDeleteAccountTask = (DeleteAccountTask) new DeleteAccountTask( mDeleteAccountTask = (DeleteAccountTask) new DeleteAccountTask(
mSelectedContextAccount.mId, mSelectedContextAccount.mId,
mSelectedContextAccount.getStoreUri(AccountFolderList.this)).execute(); mSelectedContextAccount.getStoreUri(AccountFolderList.this)).execute();
@ -603,7 +604,7 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
/** /**
* Handler for UI-thread operations (when called from callbacks or any other threads) * Handler for UI-thread operations (when called from callbacks or any other threads)
*/ */
class MessageListHandler extends Handler { private class MessageListHandler extends Handler {
private static final int MSG_PROGRESS = 1; private static final int MSG_PROGRESS = 1;
@Override @Override
@ -685,12 +686,12 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
/* package */ static class AccountsAdapter extends CursorAdapter { /* package */ static class AccountsAdapter extends CursorAdapter {
Context mContext; private final Context mContext;
private LayoutInflater mInflater; private final LayoutInflater mInflater;
private int mMailboxesCount; private final int mMailboxesCount;
private int mSeparatorPosition; private final int mSeparatorPosition;
long mDefaultAccountId; private final long mDefaultAccountId;
ArrayList<Long> mOnDeletingAccounts = new ArrayList<Long>(); private final ArrayList<Long> mOnDeletingAccounts = new ArrayList<Long>();
public static AccountsAdapter getInstance(Cursor mailboxesCursor, Cursor accountsCursor, public static AccountsAdapter getInstance(Cursor mailboxesCursor, Cursor accountsCursor,
Context context, long defaultAccountId) { Context context, long defaultAccountId) {

View File

@ -77,8 +77,8 @@ public class MailboxList extends ListActivity implements OnItemClickListener, On
private TextView mErrorBanner; private TextView mErrorBanner;
private MailboxListAdapter mListAdapter; private MailboxListAdapter mListAdapter;
private MailboxListHandler mHandler = new MailboxListHandler(); private MailboxListHandler mHandler;
private ControllerResults mControllerCallback = new ControllerResults(); private ControllerResults mControllerCallback;
// DB access // DB access
private long mAccountId; private long mAccountId;
@ -109,6 +109,8 @@ public class MailboxList extends ListActivity implements OnItemClickListener, On
super.onCreate(icicle); super.onCreate(icicle);
setContentView(R.layout.mailbox_list); setContentView(R.layout.mailbox_list);
mHandler = new MailboxListHandler();
mControllerCallback = new ControllerResults();
mListView = getListView(); mListView = getListView();
mProgressIcon = (ProgressBar) findViewById(R.id.title_progress_icon); mProgressIcon = (ProgressBar) findViewById(R.id.title_progress_icon);
mErrorBanner = (TextView) findViewById(R.id.connection_error_text); mErrorBanner = (TextView) findViewById(R.id.connection_error_text);
@ -189,22 +191,17 @@ public class MailboxList extends ListActivity implements OnItemClickListener, On
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mLoadMailboxesTask != null && Utility.cancelTaskInterrupt(mLoadMailboxesTask);
mLoadMailboxesTask.getStatus() != LoadMailboxesTask.Status.FINISHED) { mLoadMailboxesTask = null;
mLoadMailboxesTask.cancel(true); Utility.cancelTaskInterrupt(mLoadAccountNameTask);
mLoadMailboxesTask = null; mLoadAccountNameTask = null;
} Utility.cancelTaskInterrupt(mMessageCountTask);
if (mLoadAccountNameTask != null && mMessageCountTask = null;
mLoadAccountNameTask.getStatus() != LoadMailboxesTask.Status.FINISHED) {
mLoadAccountNameTask.cancel(true);
mLoadAccountNameTask = null;
}
if (mMessageCountTask != null &&
mMessageCountTask.getStatus() != MessageCountTask.Status.FINISHED) {
mMessageCountTask.cancel(true);
mMessageCountTask = null;
}
mListAdapter.changeCursor(null); mListAdapter.changeCursor(null);
mListAdapter = null;
mHandler = null;
mControllerCallback = null;
} }
public void onClick(View v) { public void onClick(View v) {

View File

@ -150,7 +150,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
private TextView mRightTitle; private TextView mRightTitle;
private Controller mController; private Controller mController;
private Listener mListener = new Listener(); private Listener mListener;
private boolean mDraftNeedsSaving; private boolean mDraftNeedsSaving;
private boolean mMessageLoaded; private boolean mMessageLoaded;
private AsyncTask mLoadAttachmentsTask; private AsyncTask mLoadAttachmentsTask;
@ -295,6 +295,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.list_title); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.list_title);
mController = Controller.getInstance(getApplication()); mController = Controller.getInstance(getApplication());
mListener = new Listener();
initViews(); initViews();
setDraftNeedsSaving(false); setDraftNeedsSaving(false);
@ -376,12 +377,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mController.removeResultCallback(mListener); mController.removeResultCallback(mListener);
} }
private static void cancelTask(AsyncTask<?, ?, ?> task) {
if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
task.cancel(true);
}
}
/** /**
* We override onDestroy to make sure that the WebView gets explicitly destroyed. * We override onDestroy to make sure that the WebView gets explicitly destroyed.
* Otherwise it can leak native references. * Otherwise it can leak native references.
@ -391,22 +386,28 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
super.onDestroy(); super.onDestroy();
mQuotedText.destroy(); mQuotedText.destroy();
mQuotedText = null; mQuotedText = null;
cancelTask(mLoadAttachmentsTask);
Utility.cancelTaskInterrupt(mLoadAttachmentsTask);
mLoadAttachmentsTask = null; mLoadAttachmentsTask = null;
cancelTask(mLoadMessageTask); Utility.cancelTaskInterrupt(mLoadMessageTask);
mLoadMessageTask = null; mLoadMessageTask = null;
// don't cancel mSaveMessageTask, let it do its job to the end. // don't cancel mSaveMessageTask, let it do its job to the end.
mSaveMessageTask = null;
// TODO Make sure the three adapters don't leak their internal cursors
if (mAddressAdapterTo != null) { if (mAddressAdapterTo != null) {
mAddressAdapterTo.changeCursor(null); mAddressAdapterTo.changeCursor(null);
mAddressAdapterTo = null;
} }
if (mAddressAdapterCc != null) { if (mAddressAdapterCc != null) {
mAddressAdapterCc.changeCursor(null); mAddressAdapterCc.changeCursor(null);
mAddressAdapterCc = null;
} }
if (mAddressAdapterBcc != null) { if (mAddressAdapterBcc != null) {
mAddressAdapterBcc.changeCursor(null); mAddressAdapterBcc.changeCursor(null);
mAddressAdapterBcc = null;
} }
mHandler = null;
mListener = null;
} }
/** /**
@ -1544,7 +1545,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
} }
} }
class Listener implements Controller.Result { private class Listener implements Controller.Result {
public void updateMailboxListCallback(MessagingException result, long accountId, public void updateMailboxListCallback(MessagingException result, long accountId,
int progress) { int progress) {
} }

View File

@ -110,9 +110,10 @@ public class MessageList extends ListActivity implements OnItemClickListener, On
private int mListFooterMode; private int mListFooterMode;
private MessageListAdapter mListAdapter; private MessageListAdapter mListAdapter;
private MessageListHandler mHandler = new MessageListHandler(); private MessageListHandler mHandler;
private Controller mController = Controller.getInstance(getApplication()); private final Controller mController = Controller.getInstance(getApplication());
private ControllerResults mControllerCallback = new ControllerResults(); private ControllerResults mControllerCallback;
private TextView mLeftTitle; private TextView mLeftTitle;
private ProgressBar mProgressIcon; private ProgressBar mProgressIcon;
@ -228,6 +229,8 @@ public class MessageList extends ListActivity implements OnItemClickListener, On
super.onCreate(icicle); super.onCreate(icicle);
setContentView(R.layout.message_list); setContentView(R.layout.message_list);
mHandler = new MessageListHandler();
mControllerCallback = new ControllerResults();
mCanAutoRefresh = true; mCanAutoRefresh = true;
mListView = getListView(); mListView = getListView();
mMultiSelectPanel = findViewById(R.id.footer_organize); mMultiSelectPanel = findViewById(R.id.footer_organize);
@ -315,28 +318,19 @@ public class MessageList extends ListActivity implements OnItemClickListener, On
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mLoadMessagesTask != null && Utility.cancelTaskInterrupt(mLoadMessagesTask);
mLoadMessagesTask.getStatus() != LoadMessagesTask.Status.FINISHED) { mLoadMessagesTask = null;
mLoadMessagesTask.cancel(true); Utility.cancelTaskInterrupt(mFindMailboxTask);
mLoadMessagesTask = null; mFindMailboxTask = null;
} Utility.cancelTaskInterrupt(mSetTitleTask);
if (mFindMailboxTask != null && mSetTitleTask = null;
mFindMailboxTask.getStatus() != FindMailboxTask.Status.FINISHED) { Utility.cancelTaskInterrupt(mSetFooterTask);
mFindMailboxTask.cancel(true); mSetFooterTask = null;
mFindMailboxTask = null;
}
if (mSetTitleTask != null &&
mSetTitleTask.getStatus() != SetTitleTask.Status.FINISHED) {
mSetTitleTask.cancel(true);
mSetTitleTask = null;
}
if (mSetFooterTask != null &&
mSetFooterTask.getStatus() != SetTitleTask.Status.FINISHED) {
mSetFooterTask.cancel(true);
mSetFooterTask = null;
}
mListAdapter.changeCursor(null); mListAdapter.changeCursor(null);
mListAdapter = null;
mHandler = null;
mControllerCallback = null;
} }
@Override @Override

View File

@ -156,9 +156,9 @@ public class MessageView extends Activity implements OnClickListener {
private Drawable mFavoriteIconOn; private Drawable mFavoriteIconOn;
private Drawable mFavoriteIconOff; private Drawable mFavoriteIconOff;
private MessageViewHandler mHandler = new MessageViewHandler(); private MessageViewHandler mHandler;
private Controller mController; private Controller mController;
private ControllerResults mControllerCallback = new ControllerResults(); private ControllerResults mControllerCallback;
private View mMoveToNewer; private View mMoveToNewer;
private View mMoveToOlder; private View mMoveToOlder;
@ -176,7 +176,7 @@ public class MessageView extends Activity implements OnClickListener {
// this is true when reply & forward are disabled, such as messages in the trash // this is true when reply & forward are disabled, such as messages in the trash
private boolean mDisableReplyAndForward; private boolean mDisableReplyAndForward;
class MessageViewHandler extends Handler { private class MessageViewHandler extends Handler {
private static final int MSG_PROGRESS = 1; private static final int MSG_PROGRESS = 1;
private static final int MSG_ATTACHMENT_PROGRESS = 2; private static final int MSG_ATTACHMENT_PROGRESS = 2;
private static final int MSG_LOAD_CONTENT_URI = 3; private static final int MSG_LOAD_CONTENT_URI = 3;
@ -346,6 +346,9 @@ public class MessageView extends Activity implements OnClickListener {
super.onCreate(icicle); super.onCreate(icicle);
setContentView(R.layout.message_view); setContentView(R.layout.message_view);
mHandler = new MessageViewHandler();
mControllerCallback = new ControllerResults();
mSubjectView = (TextView) findViewById(R.id.subject); mSubjectView = (TextView) findViewById(R.id.subject);
mFromView = (TextView) findViewById(R.id.from); mFromView = (TextView) findViewById(R.id.from);
mToView = (TextView) findViewById(R.id.to); mToView = (TextView) findViewById(R.id.to);
@ -472,22 +475,16 @@ public class MessageView extends Activity implements OnClickListener {
} }
} }
private static void cancelTask(AsyncTask<?, ?, ?> task) {
if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
task.cancel(true);
}
}
private void cancelAllTasks() { private void cancelAllTasks() {
cancelTask(mLoadMessageTask); Utility.cancelTaskInterrupt(mLoadMessageTask);
mLoadMessageTask = null; mLoadMessageTask = null;
cancelTask(mLoadBodyTask); Utility.cancelTaskInterrupt(mLoadBodyTask);
mLoadBodyTask = null; mLoadBodyTask = null;
cancelTask(mLoadAttachmentsTask); Utility.cancelTaskInterrupt(mLoadAttachmentsTask);
mLoadAttachmentsTask = null; mLoadAttachmentsTask = null;
cancelTask(mLoadMessageListTask); Utility.cancelTaskInterrupt(mLoadMessageListTask);
mLoadMessageListTask = null; mLoadMessageListTask = null;
cancelTask(mPresenceCheckTask); Utility.cancelTaskInterrupt(mPresenceCheckTask);
mPresenceCheckTask = null; mPresenceCheckTask = null;
} }
@ -505,6 +502,9 @@ public class MessageView extends Activity implements OnClickListener {
mMessageContentView = null; mMessageContentView = null;
} }
// the cursor was closed in onPause() // the cursor was closed in onPause()
mHandler = null;
mControllerCallback = null;
} }
private void onDelete() { private void onDelete() {
@ -1433,7 +1433,7 @@ public class MessageView extends Activity implements OnClickListener {
/** /**
* Controller results listener. This completely replaces MessagingListener * Controller results listener. This completely replaces MessagingListener
*/ */
class ControllerResults implements Controller.Result { private class ControllerResults implements Controller.Result {
public void loadMessageForViewCallback(MessagingException result, long messageId, public void loadMessageForViewCallback(MessagingException result, long messageId,
int progress) { int progress) {
@ -1662,7 +1662,7 @@ public class MessageView extends Activity implements OnClickListener {
private Context mContext; private Context mContext;
private MediaScannerConnection mConnection; private MediaScannerConnection mConnection;
private File mFile; private File mFile;
MessageViewHandler mHandler; private MessageViewHandler mHandler;
public MediaScannerNotifier(Context context, File file, MessageViewHandler handler) { public MediaScannerNotifier(Context context, File file, MessageViewHandler handler) {
mContext = context; mContext = context;

View File

@ -21,6 +21,7 @@ import com.android.email.Email;
import com.android.email.LegacyConversions; import com.android.email.LegacyConversions;
import com.android.email.Preferences; import com.android.email.Preferences;
import com.android.email.R; import com.android.email.R;
import com.android.email.Utility;
import com.android.email.activity.setup.AccountSettingsUtils; import com.android.email.activity.setup.AccountSettingsUtils;
import com.android.email.activity.setup.AccountSettingsUtils.Provider; import com.android.email.activity.setup.AccountSettingsUtils.Provider;
import com.android.email.mail.FetchProfile; import com.android.email.mail.FetchProfile;
@ -134,11 +135,8 @@ public class UpgradeAccounts extends ListActivity implements OnClickListener {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mConversionTask != null && Utility.cancelTask(mConversionTask, false); // false = Don't interrupt running task
mConversionTask.getStatus() != ConversionTask.Status.FINISHED) { mConversionTask = null;
mConversionTask.cancel(false);
mConversionTask = null;
}
} }
/** /**

View File

@ -16,16 +16,13 @@
package com.android.email.activity; package com.android.email.activity;
import com.android.email.Account;
import com.android.email.AccountBackupRestore; import com.android.email.AccountBackupRestore;
import com.android.email.ExchangeUtils; import com.android.email.ExchangeUtils;
import com.android.email.Preferences;
import com.android.email.activity.setup.AccountSetupBasics; import com.android.email.activity.setup.AccountSetupBasics;
import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Mailbox; import com.android.email.provider.EmailContent.Mailbox;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;