如果用到ViewPager+Fragment就必然会用到这两个类,这两个类的区别网上也讲解的很清楚。而今天我想从源码级别来学习一下为什么会有区别
他们都是有PagerAdapter派生而来,所以说他们的区别主要是他们对PagerAdapter有不同的实现方式
public abstract class FragmentStatePagerAdapter extends PagerAdapter
public abstract class FragmentPagerAdapter extends PagerAdapter
先来分析一下FragmentPagerAdapter的实现
@Override
public Object instantiateItem(ViewGroup container, int position) {
//开启事务
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
//获取item 的id
final long itemId = getItemId(position);
// Do we already have this fragment?
//这个生成的name 用与 findFragmentByTag 时用的
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) { //如果findFragmentByTag得到了fragment 直接 attach
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
//findFragmentByTag 没有找到
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
//加入事务 并设置tag 用于下次再回到这个fragment中在恢复fragment
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);
}
- 根据上面的源码来看,getItem(position)返回的fragment帮我们加入事务管理中并设置了一个name ,下次再进来这个fragment的时候直接根据这个name来恢复这个fragment。
- 在destroyItem的时候只是把这个position位置的fragment detach掉而已。
再来分析一下FragmentStatePagerAdapter的实现
这个实现相对复杂了一点,因为功能多了嘛。在分析之前必须先说一下这两个方法的实现。saveState会在我们的程序意外退出时调用,restoreState会在程序打开的时候调用,将我们在saveState中持久化的数据通过 Parcelable 对象给还原回来。有点和Activity的onSaveInstanceState原理很像。
其实这两个方法就是讲mSavedState和mFragments给保存起来,用于app在意外退出的时候恢复数据的
如果我们打开ViewPager的源码会发现下面两个方法会在onSaveInstanceState()和onRestoreInstanceState()方法中调用。这些方法都是重写View的,说明我们的每一个View都是能力在程序意外退出的时候恢复自己数据
@Override
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle)state;
bundle.setClassLoader(loader);
Parcelable[] fss = bundle.getParcelableArray("states");
mSavedState.clear();
mFragments.clear();
if (fss != null) {
for (int i=0; i<fss.length; i++) {
mSavedState.add((Fragment.SavedState)fss[i]);
}
}
Iterable<String> keys = bundle.keySet();
for (String key: keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
while (mFragments.size() <= index) {
mFragments.add(null);
}
f.setMenuVisibility(false);
mFragments.set(index, f);
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
}
}
- 核心的 instantiateItem和destroyItem方法大概可以分为这8个步骤。可见每次都会remove掉被销毁的fragment,再次显示是用remove掉之前保存的Fragment.SavedState来恢复,而且程序被意外退出时也能恢复,这个是FragmentPagerAdapter没有的。
@Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {//1.如果mFragments中有这个fragment 直接返回
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
//2.每次都会调用 getItem 得到fragment
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
//3.如果当前保存的mSavedState中有该位置的数据 setInitialSavedState 将数据恢复
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
//4.如果mFragments的元素少 mFragments加一个空元素 (这里我也不太懂为什么不用if ,请大神赐教)
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
//5.将mFragments中的元素替换 并add到事务当中
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
//6.如果mFragments的元素少 mFragments加一个空元素
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
//7.销毁该position的fragment的时候将该fragment的Fragment.SavedState保存起来 用于步骤3恢复
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
//8.remove掉fragment
mCurTransaction.remove(fragment);
}
总结
FragmentPagerAdapter与FragmentStatePagerAdapter之所以不同是因为在加入事务管理上一个是调用attach/detach,一个是add/remove.