Understanding onTrimMemory (int level)

I recently read this article in "Managing Your Application's Memory" , I highly recommend reading it if you have AndroidDev and never did.

There are many good practices, and one thing that I never learned about is the onTrimMemory (int level) method, called by the on system of each Activity / Fragment to notify of events on which memory should or may be released.

Here is a quote from this article:

Please note that your application receives the onTrimMemory () callback using TRIM_MEMORY_UI_HIDDEN only when all the user interface components of your application process are hidden from the user . This is different from onStop (), which is called when the Activity instance becomes hidden, which happens even when the user goes to another action in your application. Therefore, although you must implement onStop () to release activity resources, such as a network connection or unregister broadcast receivers , you usually should not release your user interface resources until you receive onTrimMemory (TRIM_MEMORY_UI_HIDDEN) . This ensures that if the user moves on to another action in your application, your UI’s resources are still available to quickly resume work.

I'm really interested in implementing good memory management in my application, so I'm looking forward to implementing onTrimMemory () in the right direction.

I have only a few questions:

  • onTrimMemory (TRIM_MEMORY_UI_HIDDEN) , which is called right after onStop ()?

  • What does “free UI resources” mean in this context? for example, clear the Bitmap cache or actually delete and destroy each view in the view tree? I usually kill Views in the onDestroy () or onDestroyView () methods, now I am wondering if I am doing this correctly.

  • Is there a Twin / reply callback for onTrimMemory (TRIM_MEMORY_UI_HIDDEN) ? like onCreate-onDestroy, onStart-onStop, onCreateView-onDestroyView. I ask you to understand where and how I should restore the state of the user interface after Activity / Fragment, which was brought to the forefront after onTrimMemory (TRIM_MEMORY_UI_HIDDEN) was called.

+42
android memory-management android-activity android-fragments
Oct. 16 '13 at 8:44
source share
3 answers
  • onTrimMemory with level TRIM_MEMORY_UI_HIDDEN is actually called before onStop. When onStop is called, it means the activity is really stopping, and the Android OS can kill it right away, so you should not expect that you can call the callback again on this call, with the exception of onRestart and sometimes onDestroy.

  • “let go of your user interface resources” is actually about things like caches. Normally, you don’t have to worry about managing views or user interface components, since the OS already does this, and so there are all these callbacks to create, start, pause, stop and kill activity. However, sometimes to improve performance, you need to increase memory usage, such as caching some data used by your activities. The type of resource that you must release when calling onTrimMemory, so your application uses less memory, even if it affects performance. However, you must worry about memory leaks. If your activity stops, do not forget to refer to its submissions, because it will not allow the collection of data to be collected, which will not allow the collection of the whole context and which is bad, mainly if you want your application to work for several hours or days ( for example, when implementing services).

  • No, there is no onTrimMemory correspondent callback. However, you do not need it. As I said, if you keep some resources cache for better performance, just empty it and let it grow again if necessary. If the memory level remains low, onTrimMemory can be called again soon with the same memory level. By the way, keep in mind that onTrimMemory will be called with several memory levels, and not just with TRIM_MEMORY_UI_HIDDEN.

+23
Nov 01 '13 at 3:53
source share

Implementation example

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); } @Override public 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) { } } 

Additional Information:

When your application is running: TRIM_MEMORY_RUNNING_MODERATE The device starts to run low in memory. Your application works and does not kill.

TRIM_MEMORY_RUNNING_LOW The device is operating significantly lower in memory. Your application works and does not kill, but please free up unused resources to improve system performance (which directly affects the performance of your application).

TRIM_MEMORY_RUNNING_CRITICAL The device runs very low in memory. Your application is not yet considered a killer process, but the system will begin to kill background processes if the applications do not release resources, so now you must free up non-critical resources to prevent performance degradation.

When the visibility of your application changes: TRIM_MEMORY_UI_HIDDEN The user interface of the application is no longer displayed, so now is the best time to release large resources that are used only by your user interface.

When your application process is in the LRU background list: TRIM_MEMORY_BACKGROUND The system runs on low memory, and your process is close to the top of the LRU list. Although your application process is not at high risk of being killed, the system may already kill processes on the LRU list, so you must free resources that are easy to recover, so your process will remain on the list and resume quickly when the user returns to your application.

TRIM_MEMORY_MODERATE The system runs on low memory, and your process is in the middle of the LRU list. If the system is even more limited for memory, there is a chance that your process will be killed.

TRIM_MEMORY_COMPLETE The system runs on low memory, and your process is one of the first to be killed if the system does not recover memory. You must release absolutely everything that is not critical for the resumption of the state of your application. To keep API levels below 14, you can use the onLowMemory() method as a reserve, which is roughly equivalent to the TRIM_MEMORY_COMPLETE level.

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

+7
Jan 29 '15 at 8:43
source share

I made the problem that onTrimMemory () was never called when the display was turned off. So 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 } 

This worked for me, but I'm not sure if this is the safe way. RFC

UPDATE: Launching camera intent, the app is closed, so it doesn't quite behave as desired.

Instead, this code is used. works great:

 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); } 
+1
Feb 11 '16 at 10:54 on
source share



All Articles