一. ViewPager的预加载
1 | public void setOffscreenPageLimit(int limit); |
设置预加载的页面数量limit,默认是1。但是就算设置 0,源码里面也会把你重新设为1。这里面说的数量是屏幕之外加载的数量。
同时,limit还有另外一层含义,缓存 2*limit+1
页。例如 limit = 1时,当前在第一页,会预加载第二页;滑动到第二页,会预加载第三页;当滑动到第三页,需要预加载第四页。此时,有四个页面,可我们只能缓存三个页面,所以第一个页面会被销毁。
1 | //DEFAULT_OFFSCREEN_PAGES |
二. ViewPager的懒加载
1. 为什么要懒加载?
viewPager配合fragment,默认会加载左右两个fragment,我们称之为viewpager的预加载。这种奇葩组合导致fragment的生命周期并非严格意义一样,当预加载的fragment不可见时生命周期已经执行到了onResume()。那么这就导致了一个问题,如果用户没有滑到预加载的页面,直接关闭了,那不是白白浪费了用户的流量和咋们服务器的性能开销;因此,懒加载方案应运而生了。
1. androidx之前
fragemnt提供了一个setUserVisibleHint(boolean isVisibleToUser)方法,可以获取是否可见的状态;
增加一个是否可见的标志(visibleToUser),我不能让它每次可见都加载数据吧,增加一个标记位首次加载(firstload),为了确保不出问题,增加一个view创建好的标记(viewCreated);
1 | public abstract class BaseFragment extends Fragment { |
解释:
① 由于setUserVisibleHint()凌驾于所有生命周期方法之前,fragment创建时首先会执行一次,此时第一个fragment并不可见isVisibleToUser=false,紧接着又执行了一次isVisibleToUser=true;此时,viewCreated还没有被置为true,setUserVisibleHint()方法中的isLazyLoad()并不会执行;让第一个fragment实现真正的执行isLazyLoad()在onViewCreated()方法里,因为此刻三个标志都置为true了;
② 由于预加载的原因,第二个fragment的生命周期已经执行到了onResume(),viewCreated=true已经形成,当滑动到可见时onViewCreated(),已经不会再执行了,那么onViewCreated()中的isLazyLoad()是无法生效的;但是会触发setUserVisibleHint()方法且isVisibleToUser=true,这时此方法的isLazyLoad()生效,后面的fragment同理。
③ 所以,在setUserVisibleHint()和onViewCreated()方法中都需要isVisibleToUser,这才有了成员变量visibleToUser;onViewCreated()方法中的isLazyLoad()是使第一个fragment的onLazyLoad()生效,setUserVisibleHint()方法中的isLazyLoad()是使后面的fragment的onLazyLoad()生效。
2. androidx之后
一切照搬过来后,懒加载一样没问题,但是这是过期的方法。点击进入在setUserVisibleHint()父类,推荐我们使用FragmentTransaction类的setMaxLifecycle(Fragment, Lifecycle.State)方法代替。今天不学习setMaxLifecycle,详细内容可以参考Fragment新功能,setMaxLifecycle了解一下。
那么我们该怎么懒加载呢?其实理解FragmentPagerAdapter中behavior参数就可以懂了。
1 | public FragmentPagerAdapter( FragmentManager fm, |
behavior有下面的两个值:
- BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT:表示当前显示的Fragment会被执行到onResume,而其它参与预加载Fragment的生命周期都只会执行到onStart。
- BEHAVIOR_SET_USER_VISIBLE_HINT:Fragment改变的时候,setUserVisibleHint方法会被调用,也就是这个参数其实是为了兼容以前的老代码。
到这里,很明显,懒加载方案就出来了。说笑了,既然预加载的fragment不会进入onResume,那么我们只需要创建一个是否第一次加载的变量在onResume处理一下。
public abstract class BaseFragment extends Fragment {
1 | /** |