首先,我们创建一个Activity和一个Fragment,并在Fragment的各个生命周期打好日志,并把Fragments丢进Viewpager,这里我们往Viewpager里丢了3个Fragment,够用了。
同时设置Viewpager.adapter = FragmentPagerAdapter
0.刚打开Avtivity
viewpager刚进入时刻:viewpager显示fragment1,fragment2同时被预加载,currentItem = 0
1.往左翻动一页
此时滑动一页来到第二页,currentItem = 1
,可以看到前2个fragment没有生命周期的变化,只是第三个faragment走了一遍前2个走过的路,这里大家也都知道,viewpager有个setOffscreenPageLimit()
方法,用来确认左右两边同时各加载几个页面,默认为1,这里adapter.getCount()==3
所有滑到中间的时候,所有页面都是在活跃的状态,所以前两个页面不会有生命周期的调用。
2.继续往左翻动一页
再次滑到下一页,currentItem = 2
可以看到第2、3个页面生命周期没有变化,只有第一个页面经历了onPause->onStop->onDestroyView
这么一个变化。第一个页面的视图被销毁了。
3.往右翻动一页
此时currentItem=1
,可以看到第一个页面的Fragment的视图重建了,从onCreateView
开始走一遍。不过并没有经历onAttach/onCreate。
4.按返回键退出Activity
fragment生命周期有这些变化,可以看到第2、3个页面也走过了onPause->onStop->onDestroyView
这么一个变化,由于第一个页面在上一步就销毁了view走过了这个步骤,所以接下来就是所有的Fragment经历onDestroy->onDetach
这么一个过程
接下来设置viewpager.adapter = FragmentStatePagerAdapter
0.首先,老规矩,刚打开Activity时候的状态
可以看到没有任何一点变化,跟上面同时期的状态是一毛一样的。currentItem = 0
1.往左翻动一页
可以看到,跟上面同时期还是没有any变化......currentItem = 1
2.继续往左翻动一页
好吧,咱继续翻,到currentItem = 2
,这里有区别了:可以看到第一个页面还是经过了onPause->onStop->onDestoryView
这么一个过程,不过在此之前,调用了onSaveInstanceState
来保存一些必要的信息,在此后,该fragment也随即从内存中销毁,因为经过了onDestroy->onDetach
这么一个过程,与activity脱离关系。那么我们再往右滑动一页,回到currentItem=1
的时候。
3.往右翻动一页
可以看到此时第一个页面的fragment重建了,重新走了一遍onAttach->...->onResume
这么个过程,并且在其中有传入saveInstanceState的方法中传入了一个不为null的对象,日志中我们可以看到bundle中有个testKey=0
,是因为我们重写了fragment的一个方法:
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putInt("testKey",arguments.getInt("index"))
log("onSaveInstanceState")
}
这里arguments中的int extra是自己传进去的。
那么我们看下FragmentStateViewPager
的源码:
@Override
public Object instantiateItem(ViewGroup container, int position) {
...
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
...
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
...
}
省略掉无关代码,就剩下以上这些,可以看到FragmentStateViewPager$destroyItem
中为我们保存了Fragment退出时的状态,而在FragmentStateViewPager$instantiateItem
中为新建的Fragment设置了初始化state,以此达到为我们保存Fragment state的目的。
总结 FragmentPagerAdapter&FragmentStatePagerAdapter:
相似点:
他们都无法保存视图,即在offScreenLimit之外的Fragment总是要被destroyView。
不同点:
最大的区别就是对于在offScreenLimit之外的Fragment,FragmentPagerAdapter会销毁试图,但Fragmnet不会detach,也就是Fragment还是在内存中的,当需要再次显示时他会createView,这意味着我们可以在Fragment对象中保存一些我们需要存储的信息,createView的时候做自己的选择。
而FragmentStatePagerAdapter就不同了,相同的情况下对于在offScreenLimit之外的Fragment,被destroyView只有还有detach,也就是此前的Fragment对象不复存在了,那么我们肯定不能在Fragment中保存必要的信息了,此时可以重写onSaveInstanceState来保存必要信息,并在onCreateView的时候重新拿出来用。FragmentStatePagerAdapter中为我们保存这些state是通过一个ArrayList来实现的,意味着他是记着Fragment的index作为key来保存或者取出的,
那么对于我们开发者来说,可以在此做个选择。在使用Viewpager中Fragment页面较少的情况可以选择FragmentPagerAdapter,这样代码会少几行,简单。
在viewpager中Fragment页面比较多的时候选择FragmentStatePagerAdapter来减少内存占用,Fragment试图销毁的同时detach Fargment,但需要通过saveInstaceState来达到保存关键信息的目的。
至于Fragment的生命周期,关键的应该都已经在上面出现过了,有时间再添加上hide/show/repalce时候的生命周期。