understanding onTrimMemory( int level ) understanding onTrimMemory( int level ) android android

understanding onTrimMemory( int level )


  • onTrimMemory with TRIM_MEMORY_UI_HIDDEN level is actually called before onStop. When onStop is called, it means the activity is really stopping, and the Android OS might kill it right away if it needs to, so you should not expect any more calls to that activity's callbacks aftet that, except for onRestart and sometimes onDestroy.

  • "release your UI resources" is actually about things like caches. You usually don't have to worry about managing views or UI components because the OS already does that, and that's why there are all those callbacks for creating, starting, pausing, stopping and destroying an activity. However, sometimes to improve performance you have to increase memory usage, such as caching some data used by your activities. That's the type of resource you should release when onTrimMemory is called, so your app uses less memory, even if it affects performance. You should worry about memory leaks though. If your activity stops, be sure not to keep any references to its views, because that would keep the activity from being garbage collected, which would keep the whole context from being collected, and that's bad, mostly if you want to keep your app running for hours or days (like when you implement services).

  • No, there's no correspondent callback to onTrimMemory. However, you shouldn't need one. As I said before, if you keep a cache of some resources to improve performance, just empty that, and let it grow again if it needs to. If memory level keeps low, onTrimMemory may be called again soon, with that same memory level. By the way, keep in mind that onTrimMemory will be called with several different memory levels, not just TRIM_MEMORY_UI_HIDDEN.


Sample Implementation

public class AppContext extends Application {//This my introduce OutOfMemoryException if you don't handle register and removal quiet well, better to replace it with weak reference   private static List<IMemoryInfo> memInfoList = new ArrayList<AppContext.IMemoryInfo>();public static abstract interface IMemoryInfo {        public void goodTimeToReleaseMemory();    }@Override    public void onTrimMemory(int level) {        super.onTrimMemory(level);//don't compare with == as intermediate stages also can be reported, always better to check >= or <=            if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {                try {                // Activity at the front will get earliest than activity at the                // back                for (int i = memInfoList.size() - 1; i >= 0; i--) {                    try {                        memInfoList.get(i).goodTimeToReleaseMemory();                    } catch (Exception e) {                        e.printStackTrace();                    }                }            } catch (Exception e) {                e.printStackTrace();            }        }    }/**     *      * @param implementor     *            interested listening in memory events     */    public static void registerMemoryListener(IMemoryInfo implementor) {        memInfoList.add(implementor);    }    public static void unregisterMemoryListener(IMemoryInfo implementor) {        memInfoList.remove(implementor);    }}

public class ActivityParent extends Activity implements AppContext.IMemoryInfo {    protected ActivityParent child;@Override    protected void onStop() {        super.onStop();        try {            if (child != null)                AppContext.unregisterMemoryListener(child);        } catch (Exception e) {        }    }}

public class ActivityChild extends ActivityParent {@Override    protected void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);        child = this;    }        /---move following onResume() in parent as following eg:/**@Override*       protected void onResume() {     *           super.onResume();*           if(null != child){*           AppContext.registerMemoryListener(this);*           }*       }*/        @Override        protected void onResume() {                 super.onResume();            AppContext.registerMemoryListener(this);        }@Overridepublic void goodTimeToReleaseMemory() {     super.goodTimeToReleaseMemory();//remove your Cache etc here}//--NO Need because parent implementation will be called first, just for the sake of clarity @Override    protected void onStop() {        super.onStop();        try {            if (null != child)                AppContext.unregisterMemoryListener(child);        } catch (Exception e) {        }    }

More Info:

When your app is running:TRIM_MEMORY_RUNNING_MODERATE The device is beginning to run low on memory. Your app is running and not killable.

TRIM_MEMORY_RUNNING_LOW The device is running much lower on memory. Your app is running and not killable, but please release unused resources to improve system performance (which directly impacts your app's performance).

TRIM_MEMORY_RUNNING_CRITICAL The device is running extremely low on memory. Your app is not yet considered a killable process, but the system will begin killing background processes if apps do not release resources, so you should release non-critical resources now to prevent performance degradation.

When your app's visibility changes:TRIM_MEMORY_UI_HIDDEN Your app's UI is no longer visible, so this is a good time to release large resources that are used only by your UI.

When your app's process resides in the background LRU list:TRIM_MEMORY_BACKGROUND The system is running low on memory and your process is near the beginning of the LRU list. Although your app process is not at a high risk of being killed, the system may already be killing processes in the LRU list, so you should release resources that are easy to recover so your process will remain in the list and resume quickly when the user returns to your app.

TRIM_MEMORY_MODERATE The system is running low on memory and your process is near the middle of the LRU list. If the system becomes further constrained for memory, there's a chance your process will be killed.

TRIM_MEMORY_COMPLETE The system is running low on memory and your process is one of the first to be killed if the system does not recover memory now. You should release absolutely everything that's not critical to resuming your app state.To support API levels lower than 14, you can use the onLowMemory() method as a fallback that's roughly equivalent to the TRIM_MEMORY_COMPLETE level.

http://developer.android.com/reference/android/content/ComponentCallbacks2.html


I was forcing the problem that onTrimMemory() was never called when the display turned off.Thus I tried a workaround using ActivityLifecycleCallbacks:I used a simple counter:

onActivityStarted(){    i++;}onActivityStopped(){    i--;    if(i==0) // no more open activities, thus screen probably turned off}

It worked for me but im not sure if it is a safe way. RFC

UPDATE: Starting a camera intent, the application closed, too so it is not exactly behaving as desired.

Used this code instead. works just fine:

private void registerBroadcastReceiver() {    final IntentFilter theFilter = new IntentFilter();    theFilter.addAction(Intent.ACTION_SCREEN_OFF);    BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            String strAction = intent.getAction();            if (strAction.equals(Intent.ACTION_SCREEN_OFF)) {                // do sth            }        }    };    getApplicationContext()            .registerReceiver(screenOnOffReceiver, theFilter);}