一. ViewPager的预加载

1
public void setOffscreenPageLimit(int limit);

设置预加载的页面数量limit,默认是1。但是就算设置 0,源码里面也会把你重新设为1。这里面说的数量是屏幕之外加载的数量。

同时,limit还有另外一层含义,缓存 2*limit+1 页。例如 limit = 1时,当前在第一页,会预加载第二页;滑动到第二页,会预加载第三页;当滑动到第三页,需要预加载第四页。此时,有四个页面,可我们只能缓存三个页面,所以第一个页面会被销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//DEFAULT_OFFSCREEN_PAGES
private static final int DEFAULT_OFFSCREEN_PAGES = 1;

//源码
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
//如果我们设置预加载的页面数量 < 1
//这一步强行将预加载页面数量改为 1
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
//如果我们设置预加载的页面数量 >= 1
//正常赋值
mOffscreenPageLimit = limit;
populate();
}
}

二. ViewPager的懒加载

1. 为什么要懒加载?

viewPager配合fragment,默认会加载左右两个fragment,我们称之为viewpager的预加载。这种奇葩组合导致fragment的生命周期并非严格意义一样,当预加载的fragment不可见时生命周期已经执行到了onResume()。那么这就导致了一个问题,如果用户没有滑到预加载的页面,直接关闭了,那不是白白浪费了用户的流量和咋们服务器的性能开销;因此,懒加载方案应运而生了。

1. androidx之前

  • fragemnt提供了一个setUserVisibleHint(boolean isVisibleToUser)方法,可以获取是否可见的状态;

  • 增加一个是否可见的标志(visibleToUser),我不能让它每次可见都加载数据吧,增加一个标记位首次加载(firstload),为了确保不出问题,增加一个view创建好的标记(viewCreated);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public abstract class BaseFragment extends Fragment {

/**
* 当前Fragment状态是否可见
*/
private boolean visibleToUser = false;
/**
* View是否已创建完成
*/
private boolean viewCreated = false;
/**
* 是否第一次加载数据
*/
private boolean firstLoad = true;

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewCreated = true;
isLazyLoad();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
visibleToUser = isVisibleToUser;
isLazyLoad();
}

/**
* 判断是否开始懒加载
*/
private void isLazyLoad() {
if (visibleToUser && firstLoad && viewCreated) {
firstLoad = false;
onLazyLoad();
}
}

/**
* 懒加载
*/
protected abstract void onLazyLoad();

解释:

① 由于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
2
3
4
5
public FragmentPagerAdapter(@NonNull FragmentManager fm,
@Behavior int behavior) {
mFragmentManager = fm;
mBehavior = behavior;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 是否第一次加载数据
*/
private boolean firstLoad = true;

@Override
public void onResume() {
super.onResume();
//判断是否开始懒加载
if (firstLoad) {
firstLoad = false;
onLazyLoad();
}
}

/**
* 懒加载
*/
protected abstract void onLazyLoad();

参考文章

AndroidX更新后ViewPager懒加载的前世今生