ViewPager预加载、懒加载

说到ViewPager 的缓存页面 与 预加载 与 懒加载。哎,好像懂,但是又不完全懂。模模糊糊的感觉

ViewPager 缓存页面 与 预加载

缓存 与 预加载其实是两种东西,以前我总混为一谈。

缓存页面与预加载
缓存页面是为了预加载而服务的。先缓存,再预加载的时候就能直接加载了。

setOffscreenPageLimit(翻译 set屏幕外的page的limit)

配置ViewPager的时候,可以设置setOffscreenPageLimit参数来设置缓存、预加载的数量,但是我之前的理解一只是错的。
image.png

当setOffscreenPageLimit(2)的时候

重点!!!缓存,缓存的是当前页面的左边2个和右边两个
所以算上自己,应该是有5个页面

image.png

重点!!!预加载加载的是跳转方向上的后面两个

比如从1跳转到3,那么预加载的是4.5
比如从4跳转到2,那么预加载的是1(0没有就不加载)

image.png

缓存之所以要缓存左右两边,就是为了预加载的时候能从左跳到右,预加载右,反过来可以加载左边。

预加载问题

mViewPager.setOffscreenPageLimit(0);是没有用的,源码里最小是1。这就引发了一个问题,也就是ViewPager必须预加载,想让他不加载都不行

而预加载的意思就是 当前页面为tab = 1,那么tab = 2的Fragment的生命周期都会跑完,会在那调接口、初始化等等
当setOffscreenPageLimit值设置的比较大(5),从1跳转到6的时候,先回缓存6左右两边各5个页面,然后6后面的5个页面会预加载(都会请求接口,初始化页面等等),非常耗费内存,而且这个内存根本就回收不掉。造成OOM

image.png

ViewPager源码

populate

ViewPager里关于适配Adapter 的代码全在populate()方法里。所以重点就是这个方法。

populate()里重要的方法有五个:
startUpdate准备适配(不重要)、
instantiateItem创建item数据、
destroyItem销毁item(不重要)、
setPrimaryItem设置当前item显示、
finishUpdate完成适配

这里说的是viewPager+fragment。所以item=fragment,这些方法实现都在FragmentPagerAdapter.java里

image.png

  1. instantiateItem创建item数据的时候,会将fragment设置成不可见(也就是缓存fragment不可见)。

image.png

  1. setPrimaryItem设置当前item显示

image.png

  1. finishUpdate完成适配,在完成适配的时候会调用Fragment的生命周期

image.png

总结

1.在setPrimaryItem(设置当前item显示)的时候,会设置点击的Fragment为可见的,别的都为不可见的 fragment.setUserVisibleHint();

2.在完成适配的时候会调用Fragment的生命周期

3.也就是说setUserVisibleHint()会在Fragment的生命周期之前调用,而他不属于Fragment的生命周期

懒加载

懒加载的目的就是防止ViewPager的预加载,导致Fragment在那请求接口初始化等。
或者说 — 在页面可见的时候只加载当前页面。当页面不可见的时候,停止一切正在进行的加载。

实现BaseFragment

  1. setUserVisibleHint,当页面可见的时候加载数据,不可见时停止加载

image.png

  1. 因为setUserVisibleHint在Fragment生命周期之前调用的,所以在子Fragment–加载网络数据请求–时如果用到Fragment的任何东西都会闪退。所以得加个判断

isViewCreated是否onCreateView

image.png

在onCreateView里还得手动调用一下setUserVisibleHint这个方法

image.png

  1. 但是这样还是有bug,会重复好几次调用setUserVisibleHint,因为一会儿预加载调用,一会儿缓存调用,一会儿onCreate调用。所以

页面状态的变化时才能调用。可见–>不可见 或者 不可见–>可见
所以得先存下页面当前是否可见,然后状态变化了,在调用

image.png

  1. 因为setUserVisibleHint方法不是Fragment生命周期里的方法。所以当Fragment页面跳转的时候,不会走这个方法,所以要在onResume(), onPause() 里手动调用

image.png

  1. 即使这样还有bug。当页面多层嵌套的时候,只有第一层Fragment能正常跑。后面几层的ViewPager还是会跑预加载

外面一层ViewPager + Fragment,里面还有一层ViewPager + Fragment的时候。
image.png

解决:里面那层得判断自己的parentFragment是不是可见的,可见的话自己才加载

image.png

image.png

  1. 这样还有bug,就是有时候,内层的Fragment有时候该加载的时候,不加载,所以得手动分发一下

image.png

停止的时候也一样
image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享