34

我想实现这一点: 在此处输入图像描述
我使用带有 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 上测试。

4

6 回答 6

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);
    }
}
}
于 2013-08-21T14:55:45.317 回答
14

您不应试图干涉 Android 管理您的Fragment实现的方式。的默认值setOffScreenPageLimit应该已经是 1。这意味着当内存不足时,Android 会销毁旧片段。只要您没有内存问题,就不要管它。

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

您遇到的延迟可能是由于 UI 线程上的一些密集计算。如果是,我建议将其移至例如AsyncTask. 但是,如果没有代码,它只是对可能导致问题的猜测。但是只有初始延迟表明您正在加载可能会阻塞 UI 线程的内容。

更新:查看https://stackoverflow.com/a/9646622/170781,它非常简洁地概述了ViewPager处理Fragment实例的方式。

于 2013-08-21T08:59:45.420 回答
3

FragmentStatePagerAdapter已经非常节省内存,因为它会fragments自动销毁你不需要的东西。它只是将片段的视图直接保留在当前显示项目的左右,并破坏其他项目。

示例:因此,一旦您向右滑动,它会预加载即将成为右邻片段的片段,并销毁当前显示片段左侧现在是两个插槽的片段。

于 2013-08-15T11:50:19.993 回答
1

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

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

一般来说,我建议在应用程序占用额外内存并通过 MAT(内存分析器工具)分析引用的那一刻转储应用程序的 HPROF 文件。您显然有内存泄漏问题,我非常怀疑问题在于片段本身没有被破坏。

如果您不知道如何分析内存堆,这里有一个很好的视频。我数不清有多少次它帮助我识别和消除了我的应用程序中的内存泄漏。

于 2013-08-21T08:42:18.713 回答
1

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

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

于 2013-08-20T13:19:07.700 回答
1

Override this in FragmentStatePagerAdapter, notice the slight change.

@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();
}
于 2016-02-02T09:16:29.193 回答