ViewPager 即使在设置新适配器后仍保留片段的实例



我在ViewPagerFragment的实例中遇到了一些问题。

我有一个带有 4 个片段的ViewPager(让我们称父亲(,在最后一个片段中,我还有另一个具有动态片段数量的ViewPager(并称之为 Child(。我的意思是,我根据内存中的对象列表创建子级。因此,如果列表包含 3 个物品,则子项内部将包含 3 个片段。如果在确定的时刻发生了某些事情并且我得到了一个包含 1 个对象的列表,那么我必须只用 1 个片段更新 Child。这里重要的一点是,每个 Child 的Fragment从返回的列表中都有自己的对象,并且是基于此对象创建的。

我将片段列表设置为子ViewPager的代码如下:

@Override
public void setViewPagerChildFragments(List<Fragment> fragments) {
if (fragments != null) {
DefaultStateViewPagerAdapter adapter = (DefaultStateViewPagerAdapter) mViewpagerChild.getAdapter();
if (adapter == null) {
/* In this case I use getChildFragmentManager() because 
it's inside the last fragment of the ViewPager Father*/
adapter = new DefaultStateViewPagerAdapter(getChildFragmentManager(), fragments);
mViewpagerChild.setAdapter(adapter);
} else {
adapter.setFragments(fragments); // Into this method I do notifyDataSetChanged() already
}
}
}

请注意,我尝试使用相同的适配器实例来设置片段,然后通知更改(notifyDataSetChanged()在方法内部(。如果我没有获得适配器的实例,我会创建一个新实例并将其设置为ViewPagerChild。

例如,当我使用 2 个片段设置ViewPagerChild,一段时间后我需要用 1 个片段设置它时,就会出现问题。ViewPager内部仅显示 1 个碎片,但第二个碎片仍然连接并且没有被破坏。我知道这一点,因为我做了一个测试调用getChildFragmentManager().getFragments(),我可以看到应该被摧毁的碎片仍然存在。

您可能会说这实际上不是问题,因为Garbage Collector可以删除未使用的Fragment。但是,例如,如果在某个时刻,我尝试在ViewPagerChild 中再次设置 2 个新片段,它会使用该未使用的Fragment实例而不是创建一个新实例,不幸的是它也使用相同的对象,而不是正确的新对象。

这是我DefaultStateViewPagerAdapter代码:

public class DefaultStateViewPagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<Fragment> mFragments;
private ArrayList<String> mFragmentTitles;
public DefaultStateViewPagerAdapter(FragmentManager fm) {
super(fm);
this.mFragments = new ArrayList<>();
this.mFragmentTitles = new ArrayList<>();
}
public DefaultStateViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.mFragments = (ArrayList<Fragment>) fragments;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getItemPosition(Object object) {
int index = mFragments.indexOf(object);
if (index < 0) {
index = POSITION_NONE;
}
return index;
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position);
}
public void clearFragments() {
mFragments.clear();
mFragmentTitles.clear();
}
public void setFragments(List<Fragment> fragments) {
mFragments = (ArrayList<Fragment>) fragments;
notifyDataSetChanged();
}
public void addFragment(Fragment fragment, String title) {
mFragments.add(fragment);
mFragmentTitles.add(title);
}
}

我已经尝试覆盖saveState以避免ViewPager使用旧片段的实例,例如:

@Override
public Parcelable saveState() {
return null;
}

它有效,ViewPager不再使用旧引用,但未使用的Fragment仍然附加,并导致内存泄漏。

我不知道为什么即使我设置了新适配器,ViewPager也不会破坏其碎片。有没有人遇到过这个问题?

我找到了解决方案。

解决方案非常简单。我只需要手动null设置到孩子的适配器。有了这个,ViewPager被迫摧毁每一个碎片。

因此,在Fragment父亲的onDestroyView中,我补充说:

@Override
public void onDestroyView() {
super.onDestroyView();
mViewpagerChild.removeOnPageChangeListener(mOnPaymentMethodsPageChangeListener);
mViewpagerChild.setAdapter(null); // <-- This is what I added
}

最新更新