【问题标题】:How to destroy old fragments in FragmentStatePagerAdapter如何销毁 FragmentStatePagerAdapter 中的旧片段
【发布时间】:2013-06-06 23:43:38
【问题描述】:

我想实现这个:
我将 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 中切换到新页面时,它会很快增加 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 上测试。

【问题讨论】:

  • 请提供您的解决方案@vovahost

标签: android android-fragments android-viewpager


【解决方案1】:

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

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

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

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

【讨论】:

  • 哦,只是 setOffScreenPageLimite 救救我...当我从 1 移动到第 4 和第 4 移动到第 1 时,我有 4 个片段选项卡,它删除了所有数据,但 setOffscreenLimte 恢复了该状态,非常感谢 lottttttttttttttt...
  • 你是对的。 UI线程上的计算存在一些问题
【解决方案2】:

我遇到了同样的问题。但在我的情况下,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);
    }
}
}

【讨论】:

  • 请问您在哪里调用实例化片段上的删除?
【解决方案3】:

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

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

【讨论】:

  • 内存似乎总是堆积如山,你说的理论上是对的,但没有发生。
  • 你是用 33k 调制解调器加载 50mb 位图吗? :) 我在使用 FSPA 时从未见过这样的行为。
  • 我知道我在说什么,别担心,这是我发布的一些代码参考,您可以自己查看内存堆积。 stackoverflow.com/questions/18241433/…
  • @bofredo 我有关于破坏视图的问题,我想在滑动视图时保持原样。你能帮我吗?
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

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

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

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

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

    【讨论】:

      【解决方案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();
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多