Подтвердить что ты не робот

Понимание onTrimMemory (int level)

Недавно я прочитал эту статью в "Управление памятью вашего приложения" , я настоятельно рекомендую прочитать ее, если вы AndroidDev и никогда не делали.

Есть много хороших практик, и одна вещь, о которой я никогда не узнал, - это метод onTrimMemory (int level), вызываемый системой on каждый Activity/Fragment для уведомления о событиях, на которых память должна быть или может быть выпущена.

Вот цитата из этой статьи:

Обратите внимание, что ваше приложение получает обратный вызов onTrimMemory() с помощью TRIM_MEMORY_UI_HIDDEN только тогда, когда все компоненты пользовательского интерфейса вашего приложения процесс скрывается от пользователя. Это отличается от onStop(), который вызывается, когда экземпляр Activity становится скрытый, что происходит даже тогда, когда пользователь переходит к другому действию в ваше приложение. Поэтому, хотя вы должны реализовать onStop() для выпуска ресурсы активности, такие как сетевое подключение или отмена регистрации широковещательные приемники, , вы обычно не должны выпускать ресурсы своего пользовательского интерфейса пока вы не получите onTrimMemory (TRIM_MEMORY_UI_HIDDEN). Это обеспечивает что если пользователь перейдет к другому действию в вашем приложении, ресурсы вашего UI по-прежнему доступны для быстрого возобновления работы.

Мне действительно интересно реализовать хорошее управление памятью в моем приложении, поэтому я с нетерпением жду возможности реализовать onTrimMemory() в правильном направлении.

У меня есть только несколько вопросов:

  • onTrimMemory (TRIM_MEMORY_UI_HIDDEN), который называется прямо после onStop()?

  • что означает "освободить ресурсы пользовательского интерфейса" в этом контексте? например, очистить кеш Bitmap или фактически удалить и уничтожить каждый вид в дереве просмотра? Я обычно уничтожаю Views в методах onDestroy() или onDestroyView(), теперь мне интересно, правильно ли я делаю это.

  • существует ли обратный вызов Twin/reply для onTrimMemory (TRIM_MEMORY_UI_HIDDEN)? как onCreate-onDestroy, onStart-onStop, onCreateView-onDestroyView. Я прошу понять, где и как я должен восстановить состояние пользовательского интерфейса после Activity/Fragment, который был перенесен на передний план после onTrimMemory (TRIM_MEMORY_UI_HIDDEN) был вызван.

4b9b3361

Ответ 1

  • onTrimMemory с уровнем TRIM_MEMORY_UI_HIDDEN фактически вызывается перед onStop. Когда вызывается onStop, это означает, что активность действительно прекращается, и ОС Android может сразу же ее убить, поэтому вам не следует ожидать, что на этот вызов вы сможете снова вызвать обратный вызов, за исключением onRestart и иногда onDestroy.

  • "отпустите свои ресурсы пользовательского интерфейса" на самом деле о таких вещах, как кеши. Обычно вам не нужно беспокоиться об управлении представлениями или компонентами пользовательского интерфейса, поскольку ОС уже делает это, и поэтому есть все эти обратные вызовы для создания, запуска, приостановки, остановки и уничтожения активности. Однако иногда для повышения производительности вам необходимо увеличить использование памяти, например кэширование некоторых данных, используемых вашей деятельностью. Тот тип ресурса, который вы должны выпустить при вызове onTrimMemory, поэтому ваше приложение использует меньше памяти, даже если оно влияет на производительность. Однако вы должны беспокоиться об утечке памяти. Если ваша деятельность прекращается, не забудьте ссылаться на ее представления, потому что это не позволит сбору данных быть собранным, что не позволит собирать весь контекст и что плохо, в основном, если вы хотите, чтобы ваше приложение работало в течение нескольких часов или дней (например, при внедрении служб).

  • Нет, нет корреспондентского обратного вызова onTrimMemory. Однако вам не нужен он. Как я уже говорил, если вы сохраняете кеш-память некоторых ресурсов для повышения производительности, просто опустошите это и позвольте ей снова расти, если это необходимо. Если уровень памяти остается низким, onTrimMemory может быть вызван снова в ближайшее время с тем же уровнем памяти. Кстати, имейте в виду, что onTrimMemory будет вызываться с несколькими уровнями памяти, а не только с TRIM_MEMORY_UI_HIDDEN.

Ответ 2

Пример внедрения

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) {

        }
    }

Дополнительная информация:

Когда ваше приложение запущено: TRIM_MEMORY_RUNNING_MODERATE Устройство начинает работать низко в памяти. Ваше приложение работает и не убивает.

TRIM_MEMORY_RUNNING_LOW Устройство работает значительно ниже в памяти. Ваше приложение работает и не убивает, но, пожалуйста, освободите неиспользуемые ресурсы, чтобы повысить производительность системы (что напрямую влияет на производительность вашего приложения).

TRIM_MEMORY_RUNNING_CRITICAL Устройство работает очень низко в памяти. Ваше приложение еще не считается убийственным процессом, но система начнет убивать фоновые процессы, если приложения не будут выпускать ресурсы, поэтому теперь вы должны освободить некритические ресурсы, чтобы предотвратить ухудшение производительности.

Когда меняется видимость вашего приложения: TRIM_MEMORY_UI_HIDDEN Пользовательский интерфейс приложения больше не отображается, поэтому сейчас самое подходящее время для выпуска больших ресурсов, которые используются только вашим пользовательским интерфейсом.

Когда ваш процесс приложения находится в фоновом списке LRU: TRIM_MEMORY_BACKGROUND Система работает на низкой памяти, и ваш процесс близок к началу списка LRU. Хотя ваш процесс приложения не подвергается высокому риску быть убитым, система может уже убивать процессы в списке LRU, поэтому вы должны освобождать ресурсы, которые легко восстановить, поэтому ваш процесс останется в списке и быстро возобновится, когда пользователь возвращается в ваше приложение.

TRIM_MEMORY_MODERATE Система работает на низкой памяти, и ваш процесс находится в середине списка LRU. Если система будет еще более ограничена для памяти, есть вероятность, что ваш процесс будет убит.

TRIM_MEMORY_COMPLETE Система работает на низкой памяти, и ваш процесс является одним из первых, кто будет убит, если система не восстановит память. Вы должны выпустить абсолютно все, что не критично для возобновления состояния вашего приложения. Чтобы поддерживать уровни API ниже 14, вы можете использовать метод onLowMemory() как резерв, который примерно эквивалентен уровню TRIM_MEMORY_COMPLETE.

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

Ответ 3

Я заставлял проблему, что onTrimMemory() никогда не вызывался при выключении дисплея. Таким образом, я попытался обходным путем с помощью ActivityLifecycleCallbacks: Я использовал простой счетчик:

onActivityStarted(){
    i++;
}

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

Это сработало для меня, но я не уверен, что это безопасный путь. RFC

ОБНОВЛЕНИЕ: Запуск намерения камеры, приложение закрыто, так что он не совсем ведет себя по желанию.

Вместо этого используется этот код. отлично работает:

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);
}