理解onTrimMemory(int level)

Mar*_*nci 57 android memory-management android-fragments android-activity

我最近阅读了关于管理你的应用程序内存的这篇文章,我强烈建议你阅读它,如果你是一个AndroidDev,从来没有.

有许多好的做法,我从未碰巧知道的一件事是系统在每个Activity/Fragment上调用的onTrimMemory(int level)方法,以通知应该或可以释放哪些内存的事件.

以下是该文章的引用:

请注意,只有当应用程序进程的所有UI组件对用户隐藏时,您的应用才会收到带有TRIM_MEMORY_UI_HIDDEN的onTrimMemory()回调.这与onStop()回调不同,后者在Activity实例变为隐藏时调用,即使用户移动到应用中的其他活动也会发生.因此,虽然您应该实现onStop()来释放活动资源(如网络连接)或取消注册广播接收器,但在收到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)之前,通常不应释放UI资源.这可确保如果用户从您应用中的其他活动导航回来,您的UI资源仍可用于快速恢复活动.

我真的很想在我的应用程序中实现良好的内存管理,所以我期待以正确的方式实现onTrimMemory().

我只有几个问题:

ale*_*lan 32

  • 具有TRIM_MEMORY_UI_HIDDEN级别的onTrimMemory实际上是在onStop之前调用的.当onStop被调用时,它意味着活动真的停止了,并且如果需要,Android操作系统可能会立即将其杀死,因此除了onRestart和有时onDestroy之外,你不应该期望在此之后再调用该活动的回调.

  • "释放你的UI资源"实际上是关于像缓存这样的东西.您通常不必担心管理视图或UI组件,因为操作系统已经这样做了,这就是为什么存在创建,启动,暂停,停止和销毁活动的所有回调的原因.但是,有时为了提高性能,您必须增加内存使用量,例如缓存活动使用的一些数据.这是调用onTrimMemory时应该释放的资源类型,因此您的应用程序使用的内存较少,即使它会影响性能.你应该担心内存泄漏.如果您的活动停止,请确保不要保留对其视图的任何引用,因为这会使活动不被垃圾收集,这将使整个上下文不被收集,这很糟糕,

  • 不,onTrimMemory没有相应的回调.但是,你不应该需要一个.正如我之前所说,如果你保留一些资源的缓存来提高性能,那就把它清空,如果需要,让它再次增长.如果内存级别保持低,则可以很快再次调用onTrimMemory,具有相同的内存级别.顺便说一下,请记住,将使用几个不同的内存级别调用onTrimMemory,而不仅仅是TRIM_MEMORY_UI_HIDDEN.

  • 您能否解释一下“如果您的活动停止,请确保不要保留对其视图的任何引用”? (2认同)
  • 我认为他的意思是您不应该保留对活动或其视图的静态引用。因为如果系统需要终止该活动来回收内存,它将无法释放所有保留的大小,因为存在一个静态引用,其生命周期比活动的生命周期长。那是内存泄漏。 (2认同)

AZ_*_*AZ_ 17

样本实施

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);
    }
}
Run Code Online (Sandbox Code Playgroud)
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) {

        }
    }
}
Run Code Online (Sandbox Code Playgroud)
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) {

        }
    }
Run Code Online (Sandbox Code Playgroud)

更多信息:

应用程序运行时: TRIM_MEMORY_RUNNING_MODERATE 设备开始内存不足.您的应用正在运行且无法播放.

TRIM_MEMORY_RUNNING_LOW 设备的内存运行速度要低得多.您的应用正在运行且无法播放,但请释放未使用的资源以提高系统性能(这会直接影响应用的性能).

TRIM_MEMORY_RUNNING_CRITICAL 设备内存运行极低.您的应用程序尚未被视为可填充进程,但如果应用程序未释放资源,系统将开始终止后台进程,因此您应立即释放非关键资源以防止性能下降.

当应用程序的可见性发生变化时: TRIM_MEMORY_UI_HIDDEN 您的应用程序的UI不再可见,因此现在是发布仅由UI使用的大型资源的好时机.

当您的应用程序进程位于后台LRU列表中时: TRIM_MEMORY_BACKGROUND 系统内存不足,您的进程位于LRU列表的开头附近.虽然您的应用程序进程没有被杀死的高风险,但系统可能已经杀死了LRU列表中的进程,因此您应该释放易于恢复的资源,这样您的进程将保留在列表中并在用户返回时快速恢复到你的应用程序.

TRIM_MEMORY_MODERATE 系统内存不足,您的进程接近LRU列表的中间位置.如果系统进一步受限于内存,则您的进程可能会被终止.

TRIM_MEMORY_COMPLETE 系统内存不足,如果系统现在没有恢复内存,那么您的进程是第一个被杀死的进程之一.你应该绝对释放对恢复你的app状态不重要的一切.要支持低于14的API级别,您可以将该onLowMemory()方法用作与TRIM_MEMORY_COMPLETE级别大致相同的后备.

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

  • 但如何释放内存呢?我们收到回调,但在那之后我应该做什么。请给我任何例子。怎么删除缓存。 (2认同)