Fragment懒加载分析使用

使用过ViewPager + Fragment的朋友都知道,ViewPager默认会管理前后一个Fragment,想要管理多个可以通过setOffscreenPageLimit这个方法来设置,也只有设置>1才起效果,请看源码:

setOffscreenPageLimit源码

ViewPager limit 默认值

先来看看普通的ViewPager + Fragment,也就是setOffscreenPageLimit(1)使用:
为了方便打印Log查看,这里新建三个Fragment,分别是FirstFragment、SecondFragment、ThirdFragment,都是继承了v4包的Fragment,ViewPager使用FragmentPagerAdapter。

limit 1

从打印的结果可以发现有两个很严重的问题:

  • ViewPager只管理前后一个Fragment,当滑到ThirdFragment,FirstFragment就会调用onDestroyView,来回切换就会导致Fragment不断在销毁重建view。
  • 由于Fragment不断调用onDestroyView和onCreateView,所以会不断的在请求网络调用接口。

解决办法也很简单,想要三个Fragment都不会销毁,只需要设置setOffscreenPageLimit为2就可以了,表示管理前后两个Fragment,所以Fragment不会执行onDestoryView,接口也就不会不断调用了。

看一下设置后的结果:

limit 2

从打印结果可以看到切换Fragment的确不会执行onDestoryView了,不过细心的朋友可以发现还存在个问题:

  • 初始化的时候,三个Fragment都调用了onCreateView

这意味着显示FirstFragment的时候其他两个Fragment都会请求网络调用接口,很显然还没有达到想要的结果,理想的效果是显示的时候才请求网络调用接口,请求接口后切换就无需再次请求。

其实前面的Log打印图已经暴露了,没错,我们就是要利用setUserVisibleHint这个方法来处理,这个方法有个参数isVisibleToUser代表此Fragment是否可见,单凭这个当然不行,我们需要三个参数:

  • isViewInit:代表View是否加载完成,在onCreateView方法赋值。
  • isVisible:代表Fragment是否可见,在setUserVisibleHint方法赋值。
  • isFirstLoad:代表是否第一次加载,接口调用后赋值为false。

why are you bb so much,show me the code!

public abstract class LazyFragment extends Fragment {

    private boolean isViewInit = false; // View是否加载完成
    private boolean isVisible = false; // Fragment是否可见
    private boolean isFirstLoad = true; // 是否第一次加载

    @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutId(), container, false);
        isViewInit = true;
        return view;
    }

    @Override public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        isVisible = isVisibleToUser;
        preLoadData();
    }

    private void preLoadData() {
        if (isViewInit && isVisible && isFirstLoad) {
            loadData();
            isFirstLoad = false;
        }
    }

    /**
     * Fragment布局ID
     */
    protected abstract int getLayoutId();

    /**
     * 数据处理
     */
    protected void loadData() {
    }
}

看起来貌似没问题,先看结果吧。

初次设计

What the fuck?为什么FirstFragment loadData方法第一次不执行?

FirstFragment 第一次不执行loadData原因

这是前面的分析图,FirstFragment第一次不执行loadData方法是因为setUserVisibleHint先于onCreateView执行,preLoadData只在setUserVisibleHint方法有执行,执行的时候isViewInit为false,所以FirstFragment 不执行loadData方法。

那么解决就很简单了,只需要在onCreateView也执行preLoadData就可以了。有朋友可能会有疑问,那么loadData不就执行两次了?当然不会了,我们可是有三个boolean判断的呢。

来看一下修改后的结果图:

修改后

看到这里,你是否和我一样,嘴角一仰?

国际惯例,Github地址戳我

完成了,收工(⊜‿⊜)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值