说到ViewPager 的缓存页面 与 预加载 与 懒加载。哎,好像懂,但是又不完全懂。模模糊糊的感觉
ViewPager 缓存页面 与 预加载
缓存 与 预加载其实是两种东西,以前我总混为一谈。
缓存页面与预加载
缓存页面是为了预加载而服务的。先缓存,再预加载的时候就能直接加载了。
setOffscreenPageLimit(翻译 set屏幕外的page的limit)
配置ViewPager的时候,可以设置setOffscreenPageLimit参数来设置缓存、预加载的数量,但是我之前的理解一只是错的。
当setOffscreenPageLimit(2)的时候
重点!!!缓存,缓存的是当前页面的左边2个和右边两个
所以算上自己,应该是有5个页面
重点!!!预加载加载的是跳转方向上的后面两个
比如从1跳转到3,那么预加载的是4.5
比如从4跳转到2,那么预加载的是1(0没有就不加载)
缓存之所以要缓存左右两边,就是为了预加载的时候能从左跳到右,预加载右,反过来可以加载左边。
预加载问题
mViewPager.setOffscreenPageLimit(0);是没有用的,源码里最小是1。这就引发了一个问题,也就是ViewPager必须预加载,想让他不加载都不行
而预加载的意思就是 当前页面为tab = 1,那么tab = 2的Fragment的生命周期都会跑完,会在那调接口、初始化等等
当setOffscreenPageLimit值设置的比较大(5),从1跳转到6的时候,先回缓存6左右两边各5个页面,然后6后面的5个页面会预加载(都会请求接口,初始化页面等等),非常耗费内存,而且这个内存根本就回收不掉。造成OOM
ViewPager源码
populate
ViewPager里关于适配Adapter 的代码全在populate()方法里。所以重点就是这个方法。
populate()里重要的方法有五个:
startUpdate准备适配(不重要)、
instantiateItem创建item数据、
destroyItem销毁item(不重要)、
setPrimaryItem设置当前item显示、
finishUpdate完成适配
这里说的是viewPager+fragment。所以item=fragment,这些方法实现都在FragmentPagerAdapter.java里
- instantiateItem创建item数据的时候,会将fragment设置成不可见(也就是缓存fragment不可见)。
- setPrimaryItem设置当前item显示
- finishUpdate完成适配,在完成适配的时候会调用Fragment的生命周期
总结
1.在setPrimaryItem(设置当前item显示)的时候,会设置点击的Fragment为可见的,别的都为不可见的 fragment.setUserVisibleHint();
2.在完成适配的时候会调用Fragment的生命周期
3.也就是说setUserVisibleHint()会在Fragment的生命周期之前调用,而他不属于Fragment的生命周期
懒加载
懒加载的目的就是防止ViewPager的预加载,导致Fragment在那请求接口初始化等。
或者说 — 在页面可见的时候只加载当前页面。当页面不可见的时候,停止一切正在进行的加载。
实现BaseFragment
- setUserVisibleHint,当页面可见的时候加载数据,不可见时停止加载
- 因为setUserVisibleHint在Fragment生命周期之前调用的,所以在子Fragment–加载网络数据请求–时如果用到Fragment的任何东西都会闪退。所以得加个判断
isViewCreated是否onCreateView
在onCreateView里还得手动调用一下setUserVisibleHint这个方法
- 但是这样还是有bug,会重复好几次调用setUserVisibleHint,因为一会儿预加载调用,一会儿缓存调用,一会儿onCreate调用。所以
页面状态的变化时才能调用。可见–>不可见 或者 不可见–>可见
所以得先存下页面当前是否可见,然后状态变化了,在调用
- 因为setUserVisibleHint方法不是Fragment生命周期里的方法。所以当Fragment页面跳转的时候,不会走这个方法,所以要在onResume(), onPause() 里手动调用
- 即使这样还有bug。当页面多层嵌套的时候,只有第一层Fragment能正常跑。后面几层的ViewPager还是会跑预加载
外面一层ViewPager + Fragment,里面还有一层ViewPager + Fragment的时候。
解决:里面那层得判断自己的parentFragment是不是可见的,可见的话自己才加载
- 这样还有bug,就是有时候,内层的Fragment有时候该加载的时候,不加载,所以得手动分发一下
停止的时候也一样