ViewPager FragmentPagerAdapter在系统杀死应用后重建时UI不刷新的问题
解决方案
通过重写getItemId
方法,返回Fragment的hashCode
:
@Override
public long getItemId(int position) {/*** 恢复状态重建时,新的 Fragment 不刷新UI。* 原因:instantiateItem 中通过 mFragmentManager.findFragmentByTag(name) 无法找到新的 Fragment,* 因为 name 是由 container.getId() + getItemId 组成(其中getItemId默认返回position),二者都没变化* 为了让mFragmentManager正确找到新Fragment,那就让 getItemId(即name) 与 Fragment 绑定,而不仅与position绑定* 这样上述问题得解*/return fragmentList.get(position).hashCode();
}
原理解析
一、默认机制的问题根源
在FragmentPagerAdapter
中,instantiateItem
方法会使用makeFragmentName
生成Fragment的唯一标识Tag,组合规则如下:
"android:switcher:" + viewId + ":" + getItemId(position)
- 默认行为:父类的
getItemId()
直接返回position
。当Activity重建时,相同位置的新旧Fragment生成相同的Tag,导致系统优先从FragmentManager缓存中复用旧实例,而不会调用getItem()
创建新Fragment。
二、解决方案的核心逻辑
通过重写getItemId()
,使其返回值与Fragment对象本身相关联:
@Override
public long getItemId(int position) {return fragmentList.get(position).hashCode();
}
实现效果:
- 唯一Tag生成:即使
position
相同,只要Fragment对象不同,hashCode
必然不同,新旧Fragment的Tag不同; - 阻断缓存复用:系统通过
findFragmentByTag
查找失败,强制调用getItem()
创建新Fragment实例; - 数据一致性保证:新Fragment从最新数据源初始化,避免恢复旧状态导致的UI错乱。
三、源码级验证
查看FragmentPagerAdapter.instantiateItem()
的关键逻辑:
// 生成唯一标识Tag
final long itemId = getItemId(position);
String name = makeFragmentName(container.getId(), itemId);// 优先从缓存中查找Fragment
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {mCurTransaction.attach(fragment); // 命中缓存,直接复用旧实例
} else {fragment = getItem(position); // 未命中,创建新实例mCurTransaction.add(..., name);
}
四、示例代码
public class ServerPagerAdapter extends FragmentPagerAdapter {private final List<ServerFragment> fragmentList;@Overridepublic long getItemId(int position) {return fragmentList.get(position).hashCode(); // 绑定对象哈希值}// 其他代码保持默认实现即可
}
总结
通过重写getItemId
改变Fragment的Tag生成规则,从根本上阻断了系统对旧Fragment实例的复用机制。此方案以最小代码改动实现了数据与UI的强一致性,适用于动态数据场景的ViewPager适配。但需注意:长期持有大量Fragment对象可能引发内存压力,建议结合FragmentStatePagerAdapter
使用以优化资源回收。