Как уничтожить старые фрагменты в FragmentStatePagerAdapter
Я хочу реализовать это:
![enter image description here]()
Я использую ViewPager с FragmentStatePagerAdapter.
Я начал с примера с этой страницы:
http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html
Это мой адаптер ViewPager:
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
}
Каждая страница моего ViewPager содержит ListView с некоторыми данными.
В тот момент, когда я переключаюсь на новую страницу в ViewPager, это очень быстро увеличивает оперативную память.
Как удалить старые фрагменты?
Я также использовал это, но ничего не делает:
public void destroyItem(ViewGroup container, int position, Object object) {
FragmentManager manager = ((Fragment) object).getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove((Fragment) object);
trans.commit();
super.destroyItem(container, position, object);
}
Существует также 1-2-секундная задержка после быстрого переключения на новую страницу или старую страницу. Есть ли способ удалить эту задержку. Если я переключаюсь на новую страницу и жду 2 секунды, а затем на следующем переключателе больше нет задержки.
Протестировано на Nexus 7.
Ответы
Ответ 1
Не следует пытаться помешать тому, как Android управляет вашими реализациями Fragment
. Значение по умолчанию для setOffScreenPageLimit
должно быть уже равно. Это означает, что Android будет уничтожать старые фрагменты, когда память будет работать медленно. Если у вас нет проблемы с памятью, просто оставьте ее.
Причина, по которой увеличивается ваша память, заключается в том, что Android хранит экземпляры Fragment
в памяти, чтобы иметь возможность повторно подключаться к ним, а не создавать их. Я рекомендую вам учитывать непредвиденные ситуации, когда ваши экземпляры Fragment
уничтожаются ОС, сохраняя их состояние, если это происходит, и пусть ОС выполняет свою работу.
Задержка, которую вы испытываете, может быть связана с некоторыми интенсивными вычислениями в потоке пользовательского интерфейса. Если это так, я предлагаю переместить это, например, на AsyncTask
. Однако без кода это просто догадка о том, что может вызвать проблему. Но только начальная задержка предполагает, что вы загружаете что-то, что может блокировать поток пользовательского интерфейса.
Обновить. Посмотрите fooobar.com/questions/14340/..., в котором очень подробно описывается, как ViewPager
обрабатывает экземпляры Fragment
.
Ответ 2
У меня была та же проблема. Но в моем случае ViewPager был внутри другого фрагмента. и после удаления ViewPagerFragment из FragmentManager все фрагменты из FragmentStatePagerAdapter остаются в менеджере фрагментов. поэтому после нескольких таких изменений это был OutOfMemoryError. Затем я включаю журналы FragmentManager:
FragmentManager.enableDebugLogging(true);
И обнаружил, что идентификатор каждого нового фрагмента увеличивается каждый раз.
Это происходит только с StatePagerAdapter. Чтобы решить эту проблему, я вызываю remove для каждого созданного фрагмента.
protected void dispatchOnDetach(Iterable<Fragment> fragments) {
if (fragments == null)
return;
Activity aa = getActivity();
if (aa == null)
return;
IBaseActivity ba = (IBaseActivity) aa;
if (ba.isActivityStopped())
return;
FragmentManager frMan = ba.getSupportFragmentManager();
FragmentTransaction frTr = frMan.beginTransaction();
for (Fragment fr : fragments) {
if (fr != null) {
frTr.remove(fr);
}
}
frTr.remove(this);
frTr.commit();
}
В вашем случае. если вы не изменяете ViewPager во время выполнения, возможно, что сборщик мусора не может уничтожить ваши фрагменты даже после удаления из диспетчера фрагментов из-за некоторых ссылок на них. Вы должны проверить, используют ли некоторые глобальные классы.
И для целей оптимизации вы можете кэшировать каждый экземпляр с помощью SoftReference или LruCache. Пример:
public class MyAdapter extends FragmentStatePagerAdapter {
private final LruCache<Integer, Fragment> mCache;
public MyAdapter(FragmentManager fm) {
super(fm);
mCache = new LruCache<Integer, Fragment>(10);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
return mCache.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
private class MyCache extends LruCache<Integer, Fragment> {
public MyCache(int maxSize) {
super(maxSize);
}
@Override
protected Fragment create(Integer key) {
return ArrayListFragment.newInstance(key);
}
}
}
Ответ 3
FragmentStatePagerAdapter
уже очень бережлив с памятью, поскольку он автоматически уничтожает ваш ненужный fragments
. Он просто сохраняет взгляды фрагментов прямо слева и справа от отображаемого в данный момент элемента и уничтожает остальные.
Пример: Итак, как только вы проведите пальцем в правильном направлении, он предварительно загружает скорострельный фрагмент справа и уничтожает фрагмент, который теперь содержит два слота слева от текущего отображаемого фрагмента.
Ответ 4
Я думаю, проблема не в том, что ViewPager находится в ListFragments. Какой контент вы показываете на них? Вы выделяете много изображений? Можете ли вы опубликовать код своего списка?
Я предпочел бы сделать комментарий, но поскольку у меня недостаточно очков, я надеюсь помочь, отредактировав этот ответ.
Ответ 5
Сам ViewPager имеет метод setOffscreenPageLimit
, который позволяет указать количество страниц, поддерживаемых адаптером. Таким образом, ваши фрагменты, которые находятся далеко, будут уничтожены.
Немного сложно сказать, какова может быть ваша конкретная проблема, потому что я не знаю, что делает ваш фрагмент. По звуку 1-2-секундной задержки кажется, что вы можете немного поработать над потоком пользовательского интерфейса. И что еще вы делаете в своем фрагменте, потребляющем память? Возможно, вы загружаете изображения в некоторый статический кеш памяти и не освобождаете их от удаления фрагмента? Не могли бы вы предоставить код фрагмента, чтобы я мог видеть, что он делает?
В общем, я бы порекомендовал бы сбросить файл HPROF вашего приложения в тот момент, когда ему понадобилась дополнительная память, и проанализировать ссылки через MAT (инструмент анализатора памяти). У вас явно есть проблемы с утечкой памяти, и я очень сомневаюсь, что проблема заключается в том, что сами фрагменты не уничтожаются.
Если вы не знаете, как анализировать кучу памяти, вот хороший видео. Я не могу подсчитать, сколько раз это помогало мне идентифицировать и избавляться от утечек памяти в моих приложениях.
Ответ 6
Отмените это в FragmentStatePagerAdapter, обратите внимание на небольшое изменение.
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (position >= getCount()) {
FragmentManager manager = ((Fragment) object).getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove((Fragment) object);
trans.commit();
}