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

Как уничтожить старые фрагменты в 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.

4b9b3361

Ответ 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();
}