Don't store arbitrary Context in singletons, which causes memory leak.

We have singletons that store a Context passed to getInstance().
The problem is that when we call them, we casually pass any Context at hand.
If it's an activity (which is often the case), it'll never be GCed.

This CL make them store the application context insteaed.

Change-Id: I1abcc2c08d3f8201416d6c14720f041693823b4e
This commit is contained in:
Makoto Onuki 2010-05-20 16:11:26 -07:00
parent 12e64b09d8
commit 968be441b4
6 changed files with 44 additions and 46 deletions

View File

@ -71,7 +71,7 @@ public class Controller {
private static int MESSAGEID_TO_MAILBOXID_COLUMN_MAILBOXID = 1;
protected Controller(Context _context) {
mContext = _context;
mContext = _context.getApplicationContext();
mProviderContext = _context;
mLegacyController = MessagingController.getInstance(mContext);
mLegacyController.addListener(mLegacyListener);
@ -79,7 +79,6 @@ public class Controller {
/**
* Gets or creates the singleton instance of Controller.
* @param _context The context that will be used for all underlying system access
*/
public synchronized static Controller getInstance(Context _context) {
if (sInstance == null) {

View File

@ -124,7 +124,7 @@ public class MessagingController implements Runnable {
private final Context mContext;
protected MessagingController(Context _context) {
mContext = _context;
mContext = _context.getApplicationContext();
mThread = new Thread(this);
mThread.start();
@ -133,8 +133,6 @@ public class MessagingController implements Runnable {
/**
* Gets or creates the singleton instance of MessagingController. Application is used to
* provide a Context to classes that need it.
* @param application
* @return
*/
public synchronized static MessagingController getInstance(Context _context) {
if (sInstance == null) {

View File

@ -54,7 +54,7 @@ public class SecurityPolicy {
new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false);
/**
* This projection on Account is for scanning/reading
* This projection on Account is for scanning/reading
*/
private static final String[] ACCOUNT_SECURITY_PROJECTION = new String[] {
AccountColumns.ID, AccountColumns.SECURITY_FLAGS
@ -97,7 +97,7 @@ public class SecurityPolicy {
* Private constructor (one time only)
*/
private SecurityPolicy(Context context) {
mContext = context;
mContext = context.getApplicationContext();
mDPM = null;
mAdminName = new ComponentName(context, PolicyAdmin.class);
mAggregatePolicy = null;
@ -119,7 +119,7 @@ public class SecurityPolicy {
* max password fails take the min
* max screen lock time take the min
* require remote wipe take the max (logical or)
*
*
* @return a policy representing the strongest aggregate. If no policy sets are defined,
* a lightweight "nothing required" policy will be returned. Never null.
*/

View File

@ -63,7 +63,7 @@ public abstract class Sender {
}
return (Sender) o;
}
/**
* Find Sender implementation consulting with sender.xml file.
*/
@ -98,6 +98,7 @@ public abstract class Sender {
throws MessagingException {
Sender sender = sSenders.get(uri);
if (sender == null) {
context = context.getApplicationContext();
sender = findSender(context, R.xml.senders_product, uri);
if (sender == null) {
sender = findSender(context, R.xml.senders, uri);
@ -114,10 +115,10 @@ public abstract class Sender {
return sender;
}
/**
* Get class of SettingActivity for this Sender class.
* @return Activity class that has class method actionEditOutgoingSettings().
* @return Activity class that has class method actionEditOutgoingSettings().
*/
public Class<? extends android.app.Activity> getSettingActivityClass() {
// default SettingActivity class
@ -125,32 +126,32 @@ public abstract class Sender {
}
public abstract void open() throws MessagingException;
public String validateSenderLimit(long messageId) {
return null;
}
/**
* Check message has any limitation of Sender or not.
*
*
* @param messageId the message that will be checked.
* @throws LimitViolationException
*/
public void checkSenderLimitation(long messageId) throws LimitViolationException {
}
public static class LimitViolationException extends MessagingException {
public final int mMsgResourceId;
public final long mActual;
public final long mLimit;
private LimitViolationException(int msgResourceId, long actual, long limit) {
super(UNSPECIFIED_EXCEPTION);
mMsgResourceId = msgResourceId;
mActual = actual;
mLimit = limit;
}
public static void check(int msgResourceId, long actual, long limit)
throws LimitViolationException {
if (actual > limit) {
@ -158,7 +159,7 @@ public abstract class Sender {
}
}
}
public abstract void sendMessage(long messageId) throws MessagingException;
public abstract void close() throws MessagingException;

View File

@ -38,7 +38,7 @@ import java.util.HashMap;
* making as few network connections as possible.
*/
public abstract class Store {
/**
* String constants for known store schemes.
*/
@ -60,14 +60,14 @@ public abstract class Store {
/**
* Static named constructor. It should be overrode by extending class.
* Because this method will be called through reflection, it can not be protected.
* Because this method will be called through reflection, it can not be protected.
*/
public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks)
throws MessagingException {
throw new MessagingException("Store.newInstance: Unknown scheme in " + uri);
}
private static Store instantiateStore(String className, String uri, Context context,
private static Store instantiateStore(String className, String uri, Context context,
PersistentDataCallbacks callbacks)
throws MessagingException {
Object o = null;
@ -75,7 +75,7 @@ public abstract class Store {
Class<?> c = Class.forName(className);
// and invoke "newInstance" class method and instantiate store object.
java.lang.reflect.Method m =
c.getMethod("newInstance", String.class, Context.class,
c.getMethod("newInstance", String.class, Context.class,
PersistentDataCallbacks.class);
o = m.invoke(null, uri, context, callbacks);
} catch (Exception e) {
@ -101,7 +101,7 @@ public abstract class Store {
public int mVisibleLimitDefault;
public int mVisibleLimitIncrement;
public int mAccountInstanceLimit;
// TODO cache result for performance - silly to keep reading the XML
public static StoreInfo getStoreInfo(String scheme, Context context) {
StoreInfo result = getStoreInfo(R.xml.stores_product, scheme, context);
@ -110,14 +110,14 @@ public abstract class Store {
}
return result;
}
public static StoreInfo getStoreInfo(int resourceId, String scheme, Context context) {
try {
XmlResourceParser xml = context.getResources().getXml(resourceId);
int xmlEventType;
// walk through stores.xml file.
while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
if (xmlEventType == XmlResourceParser.START_TAG &&
if (xmlEventType == XmlResourceParser.START_TAG &&
"store".equals(xml.getName())) {
String xmlScheme = xml.getAttributeValue(null, "scheme");
if (scheme != null && scheme.startsWith(xmlScheme)) {
@ -148,16 +148,16 @@ public abstract class Store {
/**
* Get an instance of a mail store. The URI is parsed as a standard URI and
* the scheme is used to determine which protocol will be used.
*
* Although the URI format is somewhat protocol-specific, we use the following
*
* Although the URI format is somewhat protocol-specific, we use the following
* guidelines wherever possible:
*
*
* scheme [+ security [+]] :// username : password @ host [ / resource ]
*
*
* Typical schemes include imap, pop3, local, eas.
* Typical security models include SSL or TLS.
* A + after the security identifier indicates "required".
*
*
* Username, password, and host are as expected.
* Resource is protocol specific. For example, IMAP uses it as the path prefix. EAS uses it
* as the domain.
@ -166,11 +166,11 @@ public abstract class Store {
* @return an initialized store of the appropriate class
* @throws MessagingException
*/
public synchronized static Store getInstance(String uri, Context context,
PersistentDataCallbacks callbacks)
throws MessagingException {
public synchronized static Store getInstance(String uri, Context context,
PersistentDataCallbacks callbacks) throws MessagingException {
Store store = sStores.get(uri);
if (store == null) {
context = context.getApplicationContext();
StoreInfo info = StoreInfo.getStoreInfo(uri, context);
if (info != null) {
store = instantiateStore(info.mClassName, uri, context, callbacks);
@ -190,10 +190,10 @@ public abstract class Store {
return store;
}
/**
* Delete an instance of a mail store.
*
*
* The store should have been notified already by calling delete(), and the caller should
* also take responsibility for deleting the matching LocalStore, etc.
* @param storeUri the store to be removed
@ -204,13 +204,13 @@ public abstract class Store {
/**
* Get class of SettingActivity for this Store class.
* @return Activity class that has class method actionEditIncomingSettings().
* @return Activity class that has class method actionEditIncomingSettings().
*/
public Class<? extends android.app.Activity> getSettingActivityClass() {
// default SettingActivity class
return com.android.email.activity.setup.AccountSetupIncoming.class;
}
/**
* Get class of sync'er for this Store class
* @return Message Sync controller, or null to use default
@ -218,7 +218,7 @@ public abstract class Store {
public StoreSynchronizer getMessageSynchronizer() {
return null;
}
/**
* Some stores cannot download a message based only on the uid, and need the message structure
* to be preloaded and provided to them. This method allows a remote store to signal this
@ -229,7 +229,7 @@ public abstract class Store {
public boolean requireStructurePrefetch() {
return false;
}
/**
* Some protocols require that a sent message be copied (uploaded) into the Sent folder
* while others can take care of it automatically (ideally, on the server). This function
@ -240,20 +240,20 @@ public abstract class Store {
public boolean requireCopyMessageToSentFolder() {
return true;
}
public abstract Folder getFolder(String name) throws MessagingException;
public abstract Folder[] getPersonalNamespaces() throws MessagingException;
public abstract void checkSettings() throws MessagingException;
/**
* Delete Store and its corresponding resources.
* @throws MessagingException
*/
public void delete() throws MessagingException {
}
/**
* If a Store intends to implement callbacks, it should be prepared to update them
* via overriding this method. They may not be available at creation time (in which case they
@ -262,13 +262,13 @@ public abstract class Store {
*/
protected void setPersistentDataCallbacks(PersistentDataCallbacks callbacks) {
}
/**
* Callback interface by which a Store can read and write persistent data.
* TODO This needs to be made more generic & flexible
*/
public interface PersistentDataCallbacks {
/**
* Provides a small place for Stores to store persistent data.
* @param key identifier for the data (e.g. "sync.key" or "folder.id")

View File

@ -176,7 +176,7 @@ public class ExchangeStore extends Store {
* Public factory. The transport should be a singleton (per Uri)
*/
public synchronized static ExchangeTransport getInstance(URI uri, Context context)
throws MessagingException {
throws MessagingException {
if (!uri.getScheme().equals("eas") && !uri.getScheme().equals("eas+ssl+") &&
!uri.getScheme().equals("eas+ssl+trustallcerts")) {
throw new MessagingException("Invalid scheme");
@ -195,7 +195,7 @@ public class ExchangeStore extends Store {
* Private constructor - use public factory.
*/
private ExchangeTransport(URI uri, Context context) throws MessagingException {
mContext = context;
mContext = context.getApplicationContext();
setUri(uri);
}