Bug 1367851 - Use ComponentCallbacks to listen for low memory notifications. r?ahunt
At the moment, we forward these from GeckoActivity, which means that they won't work if no GeckoActivity-based activity is active (can happen within our settings for example).
As of API14, we can now register an app-wide ComponentCallbacks(2) class, allowing us to easily receive low memory notifications no matter which activity is currently alive.
MozReview-Commit-ID: 5GjSjsTKxAD
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoActivity.java
@@ -30,21 +30,9 @@ public abstract class GeckoActivity exte
ANRReporter.unregister();
}
super.onDestroy();
}
public boolean isApplicationInBackground() {
return ((GeckoApplication) getApplication()).isApplicationInBackground();
}
-
- @Override
- public void onLowMemory() {
- MemoryMonitor.getInstance().onLowMemory();
- super.onLowMemory();
- }
-
- @Override
- public void onTrimMemory(int level) {
- MemoryMonitor.getInstance().onTrimMemory(level);
- super.onTrimMemory(level);
- }
}
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1275,18 +1275,16 @@ public abstract class GeckoApp extends G
// business later and dispose of the reference.
GeckoLoader.setLastIntent(intent);
// Workaround for <http://code.google.com/p/android/issues/detail?id=20915>.
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) { }
- MemoryMonitor.getInstance().init(getApplicationContext());
-
// GeckoAppShell is tightly coupled to us, rather than
// the app context, because various parts of Fennec (e.g.,
// GeckoScreenOrientation) use GAS to access the Activity in
// the guise of fetching a Context.
// When that's fixed, `this` can change to
// `(GeckoApplication) getApplication()` here.
GeckoAppShell.setContextGetter(this);
GeckoAppShell.setGeckoInterface(this);
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -229,16 +229,17 @@ public class GeckoApplication extends Ap
mIsInitialResume = true;
mRefWatcher = LeakCanary.install(this);
sSessionUUID = UUID.randomUUID().toString();
GeckoActivityMonitor.getInstance().initialize(this);
+ MemoryMonitor.getInstance().init(this);
final Context context = getApplicationContext();
GeckoAppShell.setApplicationContext(context);
HardwareUtils.init(context);
FilePicker.init(context);
DownloadsIntegration.init();
HomePanelsManager.getInstance().init(context);
--- a/mobile/android/base/java/org/mozilla/gecko/MemoryMonitor.java
+++ b/mobile/android/base/java/org/mozilla/gecko/MemoryMonitor.java
@@ -8,54 +8,58 @@ package org.mozilla.gecko;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserProvider;
import org.mozilla.gecko.home.ImageLoader;
import org.mozilla.gecko.icons.storage.MemoryStorage;
import org.mozilla.gecko.util.ThreadUtils;
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
/**
- * This is a utility class to keep track of how much memory and disk-space pressure
- * the system is under. It receives input from GeckoActivity via the onLowMemory() and
- * onTrimMemory() functions, and also listens for some system intents related to
- * disk-space notifications. Internally it will track how much memory and disk pressure
+ * This is a utility class to keep track of how much memory and disk-space pressure the
+ * system is under. It registers itself as a ComponentCallbacks to receive all onLowMemory()/
+ * onTrimMemory() notifications for our app, and also listens for some system intents related
+ * to disk-space notifications. Internally it will track how much memory and disk pressure
* the system is under, and perform various actions to help alleviate the pressure.
*
* Note that since there is no notification for when the system has lots of free memory
* again, this class also assumes that, over time, the system will free up memory. This
* assumption is implemented using a timer that slowly lowers the internal memory
* pressure state if no new low-memory notifications are received.
*
* Synchronization note: MemoryMonitor contains an inner class PressureDecrementer. Both
* of these classes may be accessed from various threads, and have both been designed to
* be thread-safe. In terms of lock ordering, code holding the PressureDecrementer lock
* is allowed to pick up the MemoryMonitor lock, but not vice-versa.
*/
-class MemoryMonitor extends BroadcastReceiver {
+class MemoryMonitor extends BroadcastReceiver implements ComponentCallbacks2 {
private static final String LOGTAG = "GeckoMemoryMonitor";
private static final String ACTION_MEMORY_DUMP = "org.mozilla.gecko.MEMORY_DUMP";
private static final String ACTION_FORCE_PRESSURE = "org.mozilla.gecko.FORCE_MEMORY_PRESSURE";
// Memory pressure levels. Keep these in sync with those in AndroidJavaWrappers.h
private static final int MEMORY_PRESSURE_NONE = 0;
private static final int MEMORY_PRESSURE_CLEANUP = 1;
private static final int MEMORY_PRESSURE_LOW = 2;
private static final int MEMORY_PRESSURE_MEDIUM = 3;
private static final int MEMORY_PRESSURE_HIGH = 4;
+ // We're living as long as the application, so keeping a static reference to it is okay.
+ @SuppressLint("StaticFieldLeak")
private static final MemoryMonitor sInstance = new MemoryMonitor();
static MemoryMonitor getInstance() {
return sInstance;
}
private Context mAppContext;
private final PressureDecrementer mPressureDecrementer;
@@ -75,30 +79,33 @@ class MemoryMonitor extends BroadcastRec
mAppContext = context.getApplicationContext();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
filter.addAction(ACTION_MEMORY_DUMP);
filter.addAction(ACTION_FORCE_PRESSURE);
mAppContext.registerReceiver(this, filter);
+ mAppContext.registerComponentCallbacks(this);
mInited = true;
}
+ @Override
public void onLowMemory() {
Log.d(LOGTAG, "onLowMemory() notification received");
if (increaseMemoryPressure(MEMORY_PRESSURE_HIGH)) {
// We need to wait on Gecko here, because if we haven't reduced
// memory usage enough when we return from this, Android will kill us.
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
GeckoThread.waitOnGecko();
}
}
}
+ @Override
public void onTrimMemory(int level) {
Log.d(LOGTAG, "onTrimMemory() notification received with level " + level);
if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
// We seem to get this just by entering the task switcher or hitting the home button.
// Seems bogus, because we are the foreground app, or at least not at the end of the LRU list.
// Just ignore it, and if there is a real memory pressure event (CRITICAL, MODERATE, etc),
// we'll respond appropriately.
return;
@@ -271,9 +278,14 @@ class MemoryMonitor extends BroadcastRec
final ContentResolver cr = mContext.getContentResolver();
mDB.expireHistory(cr, BrowserContract.ExpirePriority.AGGRESSIVE);
mDB.removeThumbnails(cr);
// TODO: drop or shrink disk caches
}
}
+
+ // Needed to implement the ComponentCallbacks2 interface - we're only really interested in the
+ // memory-related calls, though.
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) { }
}