android UI系列专题

Android ViewPager中Fragment的生命周期&

2017-11-09  本文已影响284人  Alien水哥

首先,我们创建一个Activity和一个Fragment,并在Fragment的各个生命周期打好日志,并把Fragments丢进Viewpager,这里我们往Viewpager里丢了3个Fragment,够用了。
同时设置Viewpager.adapter = FragmentPagerAdapter

0.刚打开Avtivity

viewpager刚进入时刻
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

3.按返回键退出Activity


fragment生命周期有这些变化,可以看到第2、3个页面也走过了onPause->onStop->onDestroyView这么一个变化,由于第一个页面在上一步就销毁了view走过了这个步骤,所以接下来就是所有的Fragment经历onDestroy->onDetach这么一个过程

接下来设置viewpager.adapter = FragmentStatePagerAdapter

0.首先,老规矩,刚打开Activity时候的状态

viewpager刚进入时刻
可以看到没有任何一丢丢的变化,跟上面同时期的状态是一毛一样的。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时候的生命周期。

上一篇下一篇

猜你喜欢

热点阅读