Fragment学习之 懒加载 + Fragment在ViewPager中的生命周期

Fragment学习之 懒加载 + Fragment在ViewPager中的生命周期

前言:最近做了个新项目,第一次用到了Fragment的懒加载,顺便学习了下ViewPager的预加载。

ViewPager的预加载

首先说明一下什么是ViewPager的预加载:默认情况下ViewPager会把当前Fragment的左右相邻页面预先初始化(俗称预加载),比如你当前在第二个Fragment的页面,其实左右第一和第三的Fragment已经初始化好了,这样做的好处是ViewPager左右滑动更流畅。我们可以写个简单例子(部分代码):

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

NoScrollViewPager viewPager = (NoScrollViewPager) findViewById(R.id.viewPager);
viewPager.setAdapter(new MainAdapter(getSupportFragmentManager()));
AlphaIndicator alphaIndicator = (AlphaIndicator) findViewById(R.id.alphaIndicator);
alphaIndicator.setViewPager(viewPager);
}

private class MainAdapter extends FragmentPagerAdapter {

private List<Fragment> fragments = new ArrayList<>();

public MainAdapter(FragmentManager fm) {
super(fm);
fragments.add(new Fragment01());
fragments.add(new Fragment02());
fragments.add(new Fragment03());
fragments.add(new Fragment04());
}

@Override
public Fragment getItem(int position) {
return fragments.get(position);
}

@Override
public int getCount() {
return fragments.size();
}
}
}

//Fragment01.java:
public class Fragment01 extends Fragment {

private static final String TAG = "Fragment";

@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d(TAG, "Fragment01 ==> onAttach()");
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "Fragment01 ==> onCreateView()");
//引用创建好的xml布局
View view = inflater.inflate(R.layout.item01,container,false);
return view;
}

@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "Fragment01 ==> onDestroyView()");
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Fragment01 ==> onDestroy()");
}
}

然后我们运行并打印日志:

1
2
3
4
5
6
7
8
9
10
11
07-31 08:51:29.809 1987-1987/? D/Fragment: Fragment01 ==> onAttach()
07-31 08:51:29.809 1987-1987/? D/Fragment: Fragment02 ==> onAttach()
07-31 08:51:29.809 1987-1987/? D/Fragment: Fragment01 ==> onCreateView()
07-31 08:51:29.856 1987-1987/? D/Fragment: Fragment02 ==> onCreateView()
07-31 08:51:42.110 1987-1987/? D/Fragment: Fragment03 ==> onAttach()
07-31 08:51:42.110 1987-1987/? D/Fragment: Fragment03 ==> onCreateView()

[ 07-31 08:51:42.319 81: 81 D/ ]
Socket deconnection
07-31 08:55:21.653 1987-1987/? D/Fragment: Fragment04 ==> onAttach()
07-31 08:55:21.653 1987-1987/? D/Fragment: Fragment04 ==> onCreateView

从日志中我们可以看出,当位于Fragment02时,Fragment01和Fragment03是已经初始化完成了的,viewpager预加载是3页。这种特性会导致资源浪费:

  • 预加载不可见的Fragment时,初始化数据和其页面会占用大量资源(用户很可能只看第一个,后面两个不想看了)
  • 如果当前页面内有动画或其他持续性操作,Fragment的状态切换时操作的相关问题。
    所以,为了保持良好的用户体验,我们应该在页面处于可见状态时再去初始化和加载数据。
    解决方法:封装Fragment懒加载

Fragment 懒加载

懒加载指的是当有需要的时候才去加载。我们可以重写Fragment的setUserVisibleHint()。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

public abstract class BaseLazyFragment extends Fragment {
private static String TAG = null;

//第一次可见状态,第一次不可见状态
private boolean isFirstVisible = true;
private boolean isFirstInVisible = true;
private boolean isPrepared;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TAG = this.getClass().getSimpleName();
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (getContentViewLayoutID() != 0){
return inflater.inflate(getContentViewLayoutID(), null);
}else{
return super.onCreateView(inflater, container, savedInstanceState);
}
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initView(view);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initPrepared();
}

private synchronized void initPrepared(){
if(isPrepared){
onFirstUserVisible();
}else {
isPrepared = true;
}
}

//可见状态,不可见状态
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
if(isFirstVisible){
isFirstVisible = false;
initPrepared();
}else {
onUserVisible();
}
}else {
if (isFirstInVisible){
isFirstInVisible = false;
onFirstUserInVisible();
}else {
onUserInVisible();
}
}
}

@Override
public void onDestroy() {
destroyViewAndThing();
super.onDestroy();
}

protected abstract int getContentViewLayoutID();

protected abstract void initView(View view);

//建议在此函数内初始化view或请求数据(only once的那种)
protected abstract void onFirstUserVisible();

protected abstract void onFirstUserInVisible();

//相当于onResume()
protected abstract void onUserVisible();

protected abstract void onUserInVisible();

protected abstract void destroyViewAndThing();
}

当然在这个基类中我们还可以封装一些通用的方法,比如页面跳转,吐司…

ps:不排除存在我们需要在界面不可见的时候去请求数据的需求,所以还是具体业务具体分析,然后再选择最合适的解决方案。