Intereting Posts
Портретная ориентация камеры Использование метода -canvas.drawBitmap (bitmap, src, dst, paint) Пользовательский указатель большого пальца не прозрачен на Lollipop API21 Открыть DatePickerDialog при нажатии EditText занимает два клика Как создать кнопку изображения в Android? Android: несколько видов просмотра для пользовательского представления с существующим макетом Android RecyclerView плавный прокрутки для просмотра, что оживляет их рост Разбор меню Android-ресурса XML в список объектов Как программировать простой музыкальный секвенсер для Android (Java) Вычислите размер представления списка или как рассказать об этом полностью Центральная кнопка в LinearLayout Полный рабочий пример сценария анимации трехмерных фрагментов Gmail? Звук HTML5 не воспроизводится несколько раз в устройстве Android 4.0.4 Собственный браузер Лучший вариант использования API-интерфейсов GData на Android? Как Facebook добавляет номера значков на значок приложения в Android?

Фрагменты Android в Backstack занимают слишком много памяти

ПРОБЛЕМА:

У меня есть приложение для Android, которое позволяет пользователю просматривать профиль пользователя ViewProfileFragment . Внутри ViewProfileFragment пользователь может щелкнуть по изображению, которое приведет его к StoryViewFragment где StoryViewFragment фотографии разных пользователей. Можно щелкнуть на фотографии профиля пользователя, которая перенесет их в другой экземпляр ViewProfileFragment с ViewProfileFragment нового пользователя. Если пользователь повторно нажимает на профили пользователя, кликает изображение, которое переносит их в галерею, затем нажимает на другой профиль, который фрагменты складываются в память, быстро вызывая страшный OutOfMemoryError . Вот диаграмма, описывающая то, что я описываю:

Пользователь нажимает на профиль Боба. Профиль пользователя Bob's UserA нажимает на ImageA, беря его в галерею фотографий различных пользователей (включая Боба). UserA нажимает на профиль Sue, затем на одном из ее изображений – повторы процесса и т. Д. И т. Д.

 UserA -> ViewProfileFragment StoryViewFragment -> ViewProfileFragment StoryViewFragment -> ViewProfileFragment 

Таким образом, как вы можете видеть из типичного потока, много экземпляров ViewProfileFragment и StoryViewFragment накапливаются в backstack.

СООТВЕТСТВУЮЩИЙ КОД

Я загружаю их в виде фрагментов со следующей логикой:

 //from MainActivity fm = getSupportFragmentManager(); ft = fm.beginTransaction(); ft.replace(R.id.activity_main_content_fragment, fragment, title); ft.addToBackStack(title); 

ЧТО Я ПОЛУЧИЛ

1) Я специально использую FragmentTransaction replace чтобы метод onPause срабатывал при replace . Внутри onPause я пытаюсь высвободить столько ресурсов, сколько могу (например, очистка данных в адаптерах ListView , «обнуление» переменных и т. Д.), Так что, когда фрагмент не является активным фрагментом и нажимается на стопку, Освобождается память. Но мои усилия по освобождению ресурсов – это лишь частичный успех. Согласно MAT, у меня все еще много памяти, которая потребляется GalleryFragment и ViewProfileFragment .

2) Я также удалил вызов addToBackStack (), но, очевидно, это плохой пользовательский интерфейс, потому что он не может вернуться назад (приложение просто закрывается, когда пользователь нажимает кнопку «Назад»).

3) Я использовал MAT для поиска всех объектов, которые занимают много места, и я рассматривал их различными способами внутри методов onPauseonResume ), чтобы освободить ресурсы, но они по-прежнему значительны по размеру.

Введите описание изображения здесь

4) Я также написал цикл for в обоих фрагментах onPause который устанавливает все мои ImageViews в null, используя следующую логику:

  for (int i=shell.getHeaderViewCount(); i<shell.getCount(); i++) { View h = shell.getChildAt(i); ImageView v = (ImageView) h.findViewById(R.id.galleryImage); if (v != null) { v.setImageBitmap(null); } } myListViewAdapter.clear() 

ВОПРОСОВ

1) Могу ли я игнорировать способ, позволяющий Фрагменту оставаться на заднем стекле, а также освобождать его ресурсы, чтобы цикл .replace (фрагмент) не съел всю мою память?

2) Каковы «лучшие практики», когда ожидается, что многие фрагменты могут быть загружены в заднюю часть? Как разработчик правильно справляется с этим сценарием? (Или логика в моем приложении по своей сути ошибочна, и я просто делаю это неправильно?)

Любая помощь в мозговом штурме решения этого будет весьма признательна.

Solutions Collecting From Web of "Фрагменты Android в Backstack занимают слишком много памяти"

Оказывается, что фрагменты имеют тот же жизненный цикл, что и их родительская активность. Согласно документации Фрагмента :

Фрагмент всегда должен быть встроен в действие, и жизненный цикл фрагмента напрямую зависит от жизненного цикла хост-активности. Например, когда действие приостанавливается, то есть все фрагменты в нем, и когда действие уничтожается, так же как и все фрагменты. Однако, пока действие выполняется (оно находится в состоянии возобновленного жизненного цикла), вы можете самостоятельно управлять каждым фрагментом.

Таким образом, шаг, который вы предприняли для очистки некоторых ресурсов в onPause() фрагмента, не будет срабатывать, если родительская активность не приостановится. Если у вас есть несколько фрагментов, которые загружаются родительской активностью, скорее всего, вы используете какой-то механизм для переключения, который активен.

Возможно, вы сможете решить свою проблему, не полагаясь на onPause но переопределив setUserVisibleHint на фрагмент. Это дает вам хорошее место, где можно определить, где выполнить настройку ресурсов или очистить ресурсы, когда фрагмент входит и выходит из вида (например, если у вас есть PagerAdapter, который переключается с FragmentA на FragmentB).

 public class MyFragment extends Fragment { @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { //you are visible to user now - so set whatever you need initResources(); } else { //you are no longer visible to the user so cleanup whatever you need cleanupResources(); } } } 

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

Другое предложение – получить действительно хорошее представление о выходе анализатора памяти ( MAT ) и анализа памяти в целом. Вот хорошая отправная точка . На Android очень легко протекать в памяти, поэтому, по моему мнению, необходимо ознакомиться с концепцией и как память может уйти от вас. Возможно, ваши проблемы связаны с тем, что вы не освобождаете ресурсы, когда фрагмент выходит из поля зрения, а также утечка памяти, поэтому, если вы идете по пути использования setUserVisibleHint чтобы инициировать очистку ваших ресурсов, и вы по-прежнему видите, Объем используемой памяти, тогда утечка памяти может быть преступником, поэтому убедитесь, что вы оба вышли из нее.

Трудно видеть всю картину (даже если вы показали нам много информации), без конкретного доступа к исходному коду, что, я уверен, было бы непрактичным, если не невозможным.

Это, как говорится, есть несколько вещей, которые следует иметь в виду при работе с Фрагментами. Сначала часть отказа от ответственности.

Когда Фрагменты были представлены, они звучали как лучшая идея всех времен. Возможность одновременного отображения нескольких видов деятельности. Это был пункт продажи.

Поэтому весь мир медленно начал использовать Фрагменты. Это был новый ребенок на блоке. Все использовали Фрагменты. Если вы не использовали Фрагменты, скорее всего, «вы делали это неправильно».

Спустя несколько лет и приложения позже, эта тенденция (к счастью) вернулась к большей активности, меньше фрагмента. Это обеспечивается новыми API-интерфейсами (возможность перехода между действиями без особого внимания пользователя, как это видно из API перехода и т. Д.).

Итак, вкратце: я ненавижу фрагменты . Я считаю, что это одна из худших реализаций Android всех времен, которая только завоевала популярность из-за отсутствия Transition Framework (как она существует сегодня) между действиями. Жизненный цикл фрагмента – если угодно, шар случайных обратных вызовов, которые никогда не гарантируются, когда вы их ожидаете.

(Хорошо, я немного преувеличиваю, но спросите любого разработчика Android, если у него возникли проблемы с фрагментами в какой-то момент, и ответ будет громким да).

При всем том, что говорят, Фрагменты работают. И поэтому ваше решение должно работать.

Итак, давайте начнем смотреть, кто / где может хранить эти жесткие ссылки.

Note : Я просто собираюсь разобраться с идеями о том, как отлаживать это, но вряд ли я дам прямое решение. Используйте его как ссылку.

ЧТО ПРОИСХОДИТ? : Вы добавляете фрагменты в Backstack. В задней стопке хранится твердая ссылка на Фрагмент, а не слабая или мягкая. ( Источник )

Теперь кто хранит заднюю часть? FragmentManager и … как вы уже догадались, он использует также живую ссылку ( источник ).

И, наконец, каждое действие содержит жесткую ссылку на FragmentManager.

Короче говоря: пока ваша деятельность не умрет, все ссылки на ее фрагменты будут существовать в памяти. Независимо от операций добавления / удаления, которые выполнялись на уровне Fragment Manager / backstack.

ЧТО ТЫ МОЖЕШЬ СДЕЛАТЬ? Мне приходит в голову несколько вещей.

  1. Попробуйте использовать простой загрузчик изображений / кеш-диск, такой как Picasso , если что-нибудь, чтобы убедиться, что изображения не просочились. Вы можете позже удалить его, если хотите использовать свою собственную реализацию. При всех своих недостатках Пикассо действительно прост в использовании и пришел в состояние, когда он имеет дело с памятью «правильный путь».

  2. После того, как вы удалили проблему «Я могу протекать растровые изображения» из картинки (не каламбур!), Пришло время пересмотреть жизненный цикл фрагмента. Когда вы кладете фрагмент в backstack, он не уничтожается, но … у вас есть шанс очистить ресурсы: Fragment#onDestroyView() . И здесь вы хотите убедиться, что фрагмент аннулирует любые ресурсы.

Вы не упоминаете, используют ли ваши фрагменты setRetainInstance(true) , будьте осторожны с этим, потому что они не уничтожаются / не воссоздаются, когда активность уничтожается / воссоздается (например: вращение), и все представления могут быть пропущены, если не обрабатываться должным образом ,

  1. Наконец, но это сложнее диагностировать, возможно, вам стоит вернуться к вашей архитектуре. Вы запускаете один и тот же фрагмент (viewprofile) несколько раз, вы можете рассмотреть вместо этого, повторно использовать один и тот же экземпляр и загрузить в него «нового пользователя». Backstack можно обрабатывать, отслеживая список пользователей в том порядке, в котором они загружены, поэтому вы можете перехватывать onBackPressed и перемещать «вниз» в стек, но всегда загружать новые / старые данные по мере того, как пользователь переходит. То же самое касается вашего StoryViewFragment .

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

Надеюсь, это станет отправной точкой.

Удачи.