如何在FragmentStatePagerAdapter中销毁旧片段

时间:2013-06-06 23:43:38

标签: android android-fragments android-viewpager

我想实现这个: enter image description here
我使用带有FragmentStatePagerAdapter的ViewPager 我从这个页面的例子开始:
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中的新页面时,它将非常快速地增加RAM内存 我该如何删除旧碎片?
我也用过这个,但它没有做任何事情:

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上测试过。

6 个答案:

答案 0 :(得分:14)

您不应试图干扰Android管理Fragment实施的方式。 setOffScreenPageLimit的默认值应为1。这意味着Android会在内存不足时销毁旧片段。只要你没有内存问题,就把它留下吧。

你的内存增加的原因是因为Android在内存中保留Fragment个实例以便能够重新连接到它们而不必实例化它们。我建议您考虑操作系统销毁的Fragment实例的意外事件,如果发生这种情况则保存其状态,并让操作系统完成其工作。

您遇到的延迟可能是由于UI线程上的一些密集计算造成的。如果是,我建议将其移出,例如AsyncTask。但是,如果没有代码,只需要猜测可能导致问题的原因。但是只有一个初始延迟表明你正在加载可能会阻止UI线程的东西。

更新:请查看https://stackoverflow.com/a/9646622/170781,其中非常巧妙地概述ViewPager如何处理Fragment个实例。

答案 1 :(得分:14)

我遇到了同样的问题。但在我看来,ViewPager是在另一个片段里面。从FragmentManager中删除ViewPagerFragment后,FragmentStatePagerAdapter中的所有片段都保留在片段管理器中。所以经过几次这样的改变就是OutOfMemoryError。然后我通过以下方式打开FragmentManager日志:

FragmentManager.enableDebugLogging(true);

发现每一个新片段的id都会每次都被玷污。 它只发生在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);
    }
}
}

答案 2 :(得分:3)

FragmentStatePagerAdapter已经非常节俭,因为它会自动摧毁你不需要的fragments。它只是将片段的视图直接保留在当前显示的项目的左侧和右侧,并摧毁其他项目。

示例:因此,一旦向右滑动方向,它会预先加载即将发生的右邻居片段并销毁当前显示片段左侧两个插槽的片段

答案 3 :(得分:1)

我认为问题不在于ViewPager在ListFragments上。你在他们身上展示了什么样的内容?你分配了很多图像吗?你可以发布ListFragment的代码吗?

我更愿意发表评论,但由于我没有足够的分数,我希望通过编辑此回复来提供帮助。

答案 4 :(得分:1)

ViewPager本身有一个方法setOffscreenPageLimit,允许您指定适配器保留的页数。所以远处的碎片将被摧毁。

由于我不知道你的片段是做什么的,因此有点难以说出你的特殊问题。通过1-2秒延迟的声音,您似乎可能正在UI线程上做一些工作。还有什么你在你的片段中做什么内存消耗?也许你正在将图像加载到一些静态内存缓存中,而不是在删除片段时释放它们?你能否提供你的片段代码,以便我能看到它在做什么?

一般情况下,我建议在需要额外内存并通过MAT(内存分析器工具)分析引用时,转储应用程序的HPROF文件。您显然存在内存泄漏问题,我非常怀疑问题是Fragments本身没有被破坏。

如果您不知道如何分析内存堆,这里有一个很好的video。我无法计算它在我的应用程序中识别和消除内存泄漏的次数。

答案 5 :(得分:1)

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