Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

onTrimMemory() from ComponentCallbacks2 does not get called

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()??

like image 800
portfoliobuilder Avatar asked Jul 22 '16 17:07

portfoliobuilder


People also ask

What is use of onTrimMemory () method?

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.

What is the onTrimMemory () method Android?

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.


3 Answers

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.

like image 70
CommonsWare Avatar answered Nov 03 '22 02:11

CommonsWare


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.

like image 31
Vladislav Shcherbakov Avatar answered Nov 03 '22 01:11

Vladislav Shcherbakov


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.

like image 39
pdub Avatar answered Nov 03 '22 00:11

pdub