I have implemented ComponentCallbacks2 and onTrimMemory is never getting called. I am trying to manage memory using the Application class and my custom lifecycle. Any help with this is appreciated.
public class MemoryManager implements ComponentCallbacks2 {
private static List<MemoryInfo> memInfoList = new ArrayList<>();
public interface MemoryInfo {
void releaseMemory();
}
public static void registerMemoryListener(MemoryInfo memoryInfo) {
memInfoList.add(memoryInfo);
}
public static void unregisterMemoryListener(MemoryInfo memoryInfo) {
memInfoList.remove(memoryInfo);
}
@Override
public void onTrimMemory(int level) {
Log.i("TEST", "onTrimMemory called"); // does not get called
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
try {
for (int i = memInfoList.size() - 1; i >= 0; i--) {
try {
memInfoList.get(i).releaseMemory(); // is this correct implementation?
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
break;
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
// I added logs here, it does not get reached
break;
default:
break;
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
// do nothing
}
@Override
public void onLowMemory() {
// will log when there is log memory
}
I have an application class that calls my interfaces from MemoryManager class.
public class TestApplication extends Application implements ActivityLifecycleCallback, MemoryManager.MemoryInfo {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onStart(Activity activity) {
Log.i(TAG, "onStart() called :: " + activity.getLocalClassName());
}
@Override
public void onResume(Activity activity) {
Log.i(TAG, "onResume called :: " + activity.getLocalClassName());
MemoryManager.registerMemoryListener(this);
}
@Override
public void onPause(Activity activity) {
Log.i(TAG, "onPause called :: " + activity.getLocalClassName());
}
@Override
public void onStop(Activity activity) {
Log.i(TAG, "onStop called :: " + activity.getLocalClassName());
MemoryManager.unregisterMemoryListener(this);
}
@Override
public void onDestroy(Activity activity) {
Log.i(TAG, "onDestroy called :: " + activity.getLocalClassName());
}
@Override
public void releaseMemory() {
Log.i(TAG, "releaseMemory() called");
}
My main Activity is just keeping track of the lifecycle. This is working fine. My lifecycle methods look like the following:
@Override
protected void onDestroy() {
// register lifecycle
application.onDestroy(activity);
super.onDestroy();
}
@Override
protected void onPause() {
// register lifecycle
application.onPause(activity);
super.onPause();
}
@Override
protected void onResume() {
// register lifecycle
application.onResume(activity);
super.onResume();
}
@Override
protected void onStart() {
// register lifecycle
application.onStart(activity);
super.onStart();
}
What am I missing to invoke onTrimMemory()??
onTrimMemory(): Called when the operating system has determined that it is a good time for a process to trim unneeded memory from its process. This will happen for example when it goes in the background and there is not enough memory to keep as many background processes running as desired.
The provided onTrimMemory() callback method allows your app to listen for memory related events when your app is in either the foreground or the background, and then release objects in response to app lifecycle or system events that indicate the system needs to reclaim memory.
If you look at the documentation for ComponentCallbacks2
, you will notice that it is already implemented on classes like Application
and Activity
. So, for those components, you are welcome to just override onTrimMemory()
, and it will be called as appropriate.
This should allow you to delete just about all the code from your question.
You should not implement ComponentCallbacks2
, if want to use onTrimMemory()
, you should just override onTrimMemory()
in your Application class. But if you really want to get it right in your custom class, use registerComponentCallbacks(ComponentCallbacks callback)
You can use registerActivityLifecycleCallbacks(callback);
if you want to start tracking your activities lifecycle.
Where callback is Application class which implements Application.ActivityLifecycleCallbacks
For example, you can register it in OnCreate method of your Application class.
Maybe this was obvious to others, but to me I wasted a lot of time on answers that didn't clarify this: onTrimMemory is called when the entire SYSTEM is low, not your app specifically. Well, it's also called when your app goes to background to release UI resources, but that's irrelevant to this discussion.
Your app could run out of memory allocated to its JVM well before the system deems itself low overall.
To determine if your app is low on memory, you need to look at the JVM's memory allocation. This is very simple:
long maxMem = Runtime.getRuntime().maxMemory();
long totalMem = Runtime.getRuntime().totalMemory();
long freeMem = Runtime.getRuntime().freeMemory();
long totalFreeMem = maxMem - totalMem + freeMem;
if (totalFreeMem < (maxMem * 0.2f)) {
// I'm using 20% as my threshold for "low memory". Use a value appropriate for your application.
}
That calculation is needed for totalFreeMem because totalMemory() is just the total amount of memory currently allocated to your JVM, which could be less than the maxMemory() it's allowed. And freeMemory() is the amount of free memory within the currently allocated memory, not within the max allowed.
This unfortunately means that there is no automatic callback method to know when your JVM is low on memory (that I know of), so you will need to make low memory checks in various parts of your code where you know memory is regularly being consumed and free it as appropriate.
Of course, you COULD also have the case where the system is low but your app's JVM isn't, so you DO still want to implement onTrimMemory and free memory as appropriate. Though I would think this would be a somewhat rare case for your JVM to have plenty of memory and the system thinks it's low overall.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With