ViewPager下Fragment的生命周期
为什么写这个
在网上也有很多这个例子,但是感觉讲的都不很清楚,于是想自己跑一遍来看看整个过程,话不多说,下面就直接开始,我们知道ViewPager对fragment的管理其实是通过Adapter来管理的,下面我就分情况一一介绍,首先是Adapter继承FragmentPagerAdapter时的情况。
先吐槽一下
我之前其实已经快写好了博客,但是,后面突然发现我设置Log的位置不对,设置在了super之前,这个时候方法还没执行完,只是开始执行,这时就会产生一些偏差,就是一点不同,就是在启动程序的时候的setUserVisibleHint()方法的执行时间上不同,如下:
Log语句放在super之前
showFirstFragment&&limite1-2016116Log语句放在super之后
showFirstFragment&&limite1-2016118别的时候都是一样的。
以下的测试都是将Log语句放在super后面的时候的生命周期
当Adapter继承FragmentPagerAdapter
先上Adapter的代码
public class MyAdapter extends FragmentPagerAdapter{
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Fragment getItem(int position) {
return list.get(position);
}
}
在我们使用的时候Adapter一般都是这么写的,测试的时候,我在viewpager中加了四个fragment,主要代码如下
myAdapter=new MyAdapter(getSupportFragmentManager());
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(myAdapter);
viewPager.setCurrentItem(0);
fragmentManager=getSupportFragmentManager();
我们直接看结果:
当程序启动的时候,生命周期如下:
当从第一个fragment滑到第二个fragment的时候
first- two-20161110当从第二个滑到第三个的时候
two-three-2016116当从第三个滑到第四个的时候
three-four-2016116当从第四个滑到第三个的时候
four-three-2016116当从第三个滑到第二个的时候
three-two-2016116当从第二个滑到第一个的时候
two-first-2016116由上图,我们可以得出,当程序执行的时候,viewpager加载fragment的顺序是,先onAttach当前的fragment,再向左遍历左边的fragment,遍历右完后,再向遍历,但这只是onAttach和onCreate的执行顺序,然后当OnCreateView和onActivityCreated的时候则是先遍历加载左边的,再遍历加载右边的,最后才是当前页。然后第一次的onStart、onResume的时候则是又按照onAttach和onCreate的顺序执行。为什么要说第一次的onStart、onResume,因为当屏幕关闭的时候,执行顺序还是按照OnCreateView和onActivityCreated的顺序,这里选的时间是从第一个fragment滑到第二个fragment之后,关闭屏幕,生命周期如下:
first-two&&onPause-2016116看到这里,估计很多人就很乱了,就想问fragment的加载顺序到底是什么呢?这时,我通过如下代码得到fragment的list表,代码如下:
getfragmentList=getSupportFragmentManager().getFragments();
Log.e("log", "getfragmentlist" + getfragmentList);
if(getfragmentList!=null) {
for (int i = 0; i <getfragmentList.size();i++)
Log.e("log", "getfragmentlist" +i + getfragmentList.get(i));
}
这时,运行程序,加载完所有的fragment后的list顺序,如下:
first showfragmentlist-20161110由于屏幕原因,list中的后面看不到,但是通过for循环中的输出,还是可以看出fragment加载顺序其实是
fragment2 fragment1 fragment3 fragment4,也就是onCreateView、onActivityCreated时的顺序,也就是说在onCreateView的时候建立了fragment的顺序,除了第一次,后面的生命周期都是按照这个顺序走的(除了滑动的时候),就像上面的关闭屏幕时候的生命周期一样,细心的朋友可能注意到了图片后面的1 0 2 3,那这个是什么呢?这是各个fragment对应的下标,这个是你在list中添加fragment的顺序,如代码:
public void addFragment(){
list.add(myfragment1);
list.add(myfragment2);
list.add(myfragment3);
list.add(myfragment4);
}
也就是fragemnt2对应1,fragemnt1对应0,fragemnt3对应2,fragemnt4对应3,这个顺序控制着fragment在页面中的顺序,也就是fragment1,fragment2,fragment3,fragment4。这里验证一下,修改代码,如下:
public void addFragment(){
list.add(myfragment2);
list.add(myfragment1);
list.add(myfragment3);
list.add(myfragment4);
}
运行程序,加载完所有的fragment后的list的顺序,如下:
showFragmentList&&first&&curr0,limit1, not order-20161110这里就是fragment1对应1,fragment2对应0,fragment3对应2,fragment4对应3,页面中的fragment顺序是fragment2,fragment1,fragment3,fragment4。
还是拿之前的在list中添加fragment的顺序来进行下面的测试,也就是fragemnt2对应1,fragemnt1对应0,fragemnt3对应2,fragemnt4对应3,即fragment在页面中的顺序,也就是fragment1,fragment2,fragment3,fragment4来继续说,因为我们设置了** viewPager.setCurrentItem(0);,所以我们先onAttach ,onCreate fragemnt1,又设置了 viewPager.setOffscreenPageLimit(1);,所以再
onAttach ,onCreate fragemnt2,那么为什么先onCreateView onActivityCreated fragment2呢?这是因为viewpager的加载机制,会自动的将左右两边加载出来,所以,一下子就会加载两个,为了确保所有的view都创建,所以,将最开始要显示的view放在最后,即把fragment2的onCreateView,onActivityCreated放在fragment1的后面,那么,为什么fragment1的onStart,onResume先执行呢?这是为了用户体验,即将当前需要显示的先显示,因为这个时候是fragment1先显示。这里有一个奇怪的地方,就是第二个滑到第三个和第三个滑到第二个的图片**,第二个滑到第三个的时候,fragment1是先执行onPause,onStop,onDestroy方法,再执行fragment4的onCreateView,onActivityCreated,onStart,onResume方法,而第三个滑到第二个的时候,则是先执行fragment1的onCreateView,onActivityCreated再执行fragemnt4的onPause,onStop,onDestroy方法,最后再执行onStart,onResume方法。为什么呢?这里先放一下,先验证上面的猜想,将代码修改一下
myAdapter=new MyAdapter(getSupportFragmentManager());
viewPager.setOffscreenPageLimit(2);
viewPager.setAdapter(myAdapter);
viewPager.setCurrentItem(1);
fragmentManager=getSupportFragmentManager();
结果如下:
所以,这个顺序也验证了我们之前的想法,但是那个奇怪的地方还没解决,我们再修改代码:
myAdapter=new MyAdapter(getSupportFragmentManager());
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(myAdapter);
viewPager.setCurrentItem(1);
fragmentManager=getSupportFragmentManager();
程序运行结果如下:
firstShowFragmentcurr1limit1-2016116因为我们显示的就是第二个页面,所以从第二个页面滑到第三个,效果如下:
two-three&&curr1limit1-2016116从第三个滑到第二个,效果如下:
我们再加载完所有的fragment,再从第二个滑到第三个试试,就是不执行onAttach和onCreate,这样看的舒服一点,结果如下:
two-three&&curr1limit1loadallfragment-2016117
可以看到,执行顺序其实是类似的。也就是,当fragemnt3滑到fragemnt2的时候,fragemnt1的onCreateView和onActivityCreated会在fragemnt4的onPause,onStop,onDestroyView,先执行,然后再执行fragment1的onStart,onResume,为什么呢?我觉得,为了便以理解,可以将fragment的生命周期分成四部分,第一部分就是onAttach,onCreate。第二部分是onCreateView,onActivityCreated。第三部分是onStart,onResume。第四部分就是onPause,onStop,onDestroyView。因为,fragmentPagerAdapter只会销毁视图,所以后面的onDestroy,onDetach就不用管,当onAttach,onCreate之后,会先确定一个顺序,这里称为顺序2,也就是中左右,但是当onCreateView执行后,就确定了顺序3,这个顺序3就是getSupportFragmentManager().getFragments();
得到的list的顺序,后面的生命周期(除了第一次启动以及左右滑动时的生命周期)都是按照这个顺序执行的,也就是左右中,为什么说除了第一次启动以及左右滑动时的生命周期,这里先说第一次启动,因为,在第一次启动后的onStart和onResume都是按照onAttach的顺序,而不是按照fragment的顺序,左右滑动的生命周期是按照你在viewPager中给添加fragment的顺序,这一点后面解释,确定了fragment顺序后,排在链表前面的fragment的方法肯定在排在后面的fragment方法之前,这一点很重要,举个例子,就比如上面的关闭屏幕那张生命周期图片,因为链表的顺序,所以生命周期会是这样。这里再解释上面的滑动顺序,这里贴上我添加fragment的代码
list.add(myfragment1);
list.add(myfragment2);
list.add(myfragment3);
list.add(myfragment4);
可以看出顺序是fragment1,2,3,4。这个顺序就是顺序1,顺序1是在滑动的情况会用到。为什么我会将之前的顺序命名为顺序2,因为这个顺序是最开始你在程序中通过add(fragment)确定的顺序,在滑动的时候,谁在前面谁先生成视图或者先销毁,也就是谁在前面谁先执行,这里需要明确一点就是,任何fragment的第一部分生命周期肯定运行在别的fragment的除第一部分生命周期的生命周期前面,这一点很重要,同时还有一点,当既有fragment视图生成又有fragment视图销毁的时候,其实就是滑动的时候,对于生成视图的那个fragment的第一部分生命周期和第二部分的生命周期不会同时进行,这里解释后面一点,修改代码:
myAdapter=new MyAdapter(getSupportFragmentManager());
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(myAdapter);
viewPager.setCurrentItem(3);
当第一次从3滑到2的时候生命周期如图:
thrid-second&& curr3,limit1-20161110因为顺序1是fragment1,2,3,4。所以应该是fragment1先执行第一部分,再执行第二部分生成视图,再fragment4执行销毁视图,再fragment1执行第三部分。但是结果却是这个,说明当既有fragment视图生成又有fragment视图销毁的时候,其实就是滑动的时候,对于生成视图的那个fragment的第一部分生命周期和第二部分的生命周期不会同时进行。
看第二个滑到第三个,第三个滑到第二个这个情况,先看第一次第二个滑到第三个的情况,因为,任何fragment的第一部分生命周期肯定运行在别的fragment的除第一部分生命周期的生命周期前面,同时哦,对于生成视图的那个fragment的第一部分生命周期和第二部分的生命周期不会同时进行,又因为在顺序1中,fragment1在fragment4的前面,所以,fragment1先销毁,fragment4再生成和显示,所以,先执行fragment1的onAttach,onCreate,再执行fragment1的onPause,onStop,onDestroyView,再执行fragment4的onCreateView,onActivityCreated。而在第三个滑到第二个时,因为,不需要onAttach和onCreate,又因为在顺序1中,fragment1在fragment4的前面,所以,会先执行fragment1的onCreateView,onActivityCreated,再执行fragment4的onPause,onStop,onDestroyView。然后到显示的时候,再执行fragment1的onStart,onResume。为什么不会直接运行fragment1的onCreateView,onActivityCreated,onStart,onResume呢?因为viewPager.setOffscreenPageLimit(1);方法的限制,所以只有先将销毁视图的fragment的视图销毁,才能显示,所以接下来执行fragment4的onPause,onStop,onDestroyView,最后执行fragment1的onStart,onResume。然后,再一次从第二个滑到第三个的情况,因为,不需要onAttach和onCreate,又因为在顺序1中,fragment1在fragment4的前面,所以,fragment1先销毁,fragment4再生成和显示,所以,先执行fragment1的onPause,onStop,onDestroyView,再执行fragment4的onCreateView,onActivityCreated,onStart,onResume。这里可能有人会说,为什么不是按照顺序2来判断,因为在顺序2中,fragment1也是在fragment4之前呀,这里还是原来的代码:
myAdapter=new MyAdapter(getSupportFragmentManager());
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(myAdapter);
viewPager.setCurrentItem(3);
这时,顺序1是fragment1,2,3,4.而顺序2变成fragment4,3,2,1。这两个顺序中fragment1和fragment4顺序不一样,看从2滑到3的生命周期,如图:
showSecondFragment&& curr0 limit1-2016118
第一个滑到第二个
first-second&&second,curr0,limit1-2016117第二个滑到第三个
second-thrid&& second,curr0,limit1-20161110第三个滑到第四个
thrid-fouth&&second,curr0,limit1-2016117第四个滑到第三个
fouth-thrid&&second,curr0,limit1-2016117第三个滑到第二个
thrid-second&&second,curr0,limit1-2016117第二个滑到第一个
second-first&&second,curr0,limit1-2016117第一个滑到第二个后按Pause
first-second&&second,curr0,limit1,pause-2016117第一个滑到第二个后的fragemntList
first-secondShowFragmentList&&second,curr0,limit1,-2016117第二个滑到第三个后的fragemntList
two-threeShowFragmentList&& second,curr0,limit1-20161110第三个滑到第二个后的fragmentList
thrid-secondShowFragmentList&& Second,curr0,limit1-20161110从上图可以看出,跟FragmentPagerAdapter相比,FragmentStatePagerAdapter每次销毁的时候是直接remove,所以会在销毁之后重新显示的话,就会执行fragment的所有周期函数来显示。
FragmentPagerAdapter的destroyItem()方法
@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);
}
FragmentStatePagerAdapter的destroyItem()方法
@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());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
}
注意最后两张图片中的list,分别有一个是空的,这也就说明,这里的fragment为空,但是依然占着位置,下次生成这个的时候,直接插入到这个位置,所以顺序2还是不会改变。同时,也没有后面的1 0 2 3,同时,上面划分的Fragment生命周期四部分也不一样,最后一部分应该再加上onDestroy,onDetach。生命周期的差异就体现在当有销毁又有生成fragment的视图的时候,这个时候跟FragmentPagerAdapter不一样,都是生成视图的fragment的第一部分先执行,再销毁视图的fragment去销毁视图,再生成视图的fragment显示。其实这就是上面的规则3。
当使用懒加载的时候呢?
很明显生命周期会是一样的,因为设置懒加载的操作,并没有影响生命周期,所以生命周期是一样的,只是懒加载保证了显示哪页加载哪页的数据,有兴趣的朋友可以自己测试一下,毕竟纸上得来终觉浅,绝知此事要躬行。