这可能是讲的最透彻的Fragment懒加载了

没错,就是这样自信。

首先,我们要明白,我们平常所说的Fragment懒加载是指数据的懒加载,而不是指Fragment本身的懒加载;同时,懒加载这个说法,需要Fragment和ViewPager结合起来用,才有懒加载一说。

为什么这么说呢,因为在ViewPager结合Fragment使用的时候,google为了让用户体验更好,默认是初始化了两个Fragment,当滑动到第二个Fragmet的时候,由于已经初始化了,所以界面展示更快,提升了用户体验。但这就引起了一个问题,由于是默认初始化了两个Fragment,两个Fragment都会去请求数据,如果两个Fragment请求的数据过多,而且我只看了一个Fragment的数据,这就造成资源的浪费,所以懒加载(数据的懒加载)就出现了。

那什么是懒加载呢?就是在不可见的Fragment初始化的时候,不去请求数据,当它对用户可见的时候再去请求,这就是所谓的懒加载。

上面提到了ViewPager结合Fragment的时候,默认初始化了两个Fragment。那有人问,你怎么知道是默认初始化了两个?能不能有方法控制默认初始化的个数?第一个问题,肯定是看源码知道的啊。第二个问题,肯定是可以控制初始化的个数的啊。

首先我们看ViewPager的一个方法: 

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);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }

这个方法就是控制默认初始化Fragment的个数的。一目了然,默认的离屏的Fragment个数是1,再加上当前的Fragment,默认就初始化了两个。那有人说了,我将默认离屏的Fragment个数设置成0,不就只初始化当前这一个Fragment了吗?想的太简单,看下这个方法里面,是做了判断的;当传入的个数小于默认值1时,还是使用默认值1。现在应该明白这个方法是干嘛的了吧。

那怎么实现懒加载呢,别急,要实现懒加载,我们需要先明白Fragment的生命周期才可以。

接下来我们来看个例子。我们定义了四个Fragment,Fragment1,Fragment2,Fragment3,Fragment4,将这个4个Fragment都放入ViewPager中,当进入第一个Fragment1的时候,我们看下Fragment1的生命周期。

Fragment1 setUserVisibleHint isVisibleToUser=false
Fragment1 setUserVisibleHint isVisibleToUser=true
Fragment1 onAttach
Fragment1 onCreate
Fragment1 onViewCreated
Fragment1 onActivityCreated

这里只罗列了部分的生命周期,在生命周期中,我们发现了一个方法setUserVisibleHint,它是优先于其他所有的生命周期方法。这个方法会在Fragment不可见和可见的时候调用,当不可见的时候,它有一个变量isVisibleToUser,返回值为false,可见的时候,返回值为true,这个方法就是实现懒加载的关键方法。

现在我们看下当进入第一个Fragment1,并且没有调用setOffscreenPageLimit方法的时候的生命周期:

这里有两个Fragment的生命周期,默认初始化了Fragment1和Fragment2两个Fragment,注意前三行,由于最开始Fragment1对用户不可见,所以isVisibleToUser为false,初始化了Fragment2,它对用户也不可见,所以isVisibleToUser也为false,但是当Fragment1对我们可见的时候就返回true了。

接着,我们滑动到第二个Fragment,即Fragment2,我们再看下生命周期的方法:

现在看一下,Fragment3,它目前对用户不可见,但是还是初始化了,不过它的isVisibleToUser为false,Fragment2对用户可见,它的isVisibleToUser就变成true了,而Fragment1这时也是不可见的,所以它的isVisibleToUser就变成false了。同理,我们再滑到到Fragment3,看下生命周期

和上面的一模一样,只是Fragment4这个时候初始化了。

按照上面的生命周期,我们就可以实现懒加载了,我们定义一个方法lazyLoadDataIfPrepared(),它来执行懒加载,但它在什么时候调用呢?

1、在第二个Fragment的生命周期的setUserVisibleHint(isVisibleToUser: Boolean)方法中,由于当isVisibleToUser为true的时候需要去请求数据,这样,就相当于Fragment2对用户可见时再去请求数据了。所以lazyLoadDataIfPrepared这个方法应该在setUserVisibleHint(isVisibleToUser: Boolean)方法中并且isVisibleToUser为true的时候调用。

2、接着,我们需要在页面渲染完成之后再去请求数据,所以请求数据的方法应该也要放在onViewCreated生命周期中,所以也需要在onViewCreated方法中调用lazyLoadDataIfPrepared方法。

3、但是在来回滑动的过程中,肯定是不需要再次去请求数据了,如果在setUserVisibleHint去请求数据,那么来回滑动Fragment,都还是要请求数据,所以我们可以用一个变量hasLoadData控制是否已经加载过数据了,加载之后,hasLoadData就置为true。

考虑到以上三种情况,我们还需要在lazyLoadDataIfPrepared方法做一个判断,当hasLoadData为false(表示未加载过数据),执行了onViewCreated生命周期,并且getUserVisibleHint为true的时候,再去执行真正的数据加载。说了这么多,看下完整的源码就知道了。

abstract class BaseFragment: Fragment() {

     private val TAG = this::class.java.name

    /**
     * 视图是否加载完毕
     */
    private var isViewPrepare = false
    /**
     * 数据是否加载过了
     */
    private var hasLoadData = false

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(getLayoutId(),null)
        Log.d(TAG, "$TAG onCreateView")
    }



    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        Log.d(TAG, "$TAG setUserVisibleHint isVisibleToUser=$isVisibleToUser")
        if (isVisibleToUser) {
            lazyLoadDataIfPrepared()
        }
    }

     override fun onAttach(context: Context?) {
         super.onAttach(context)
         Log.d(TAG, "$TAG onAttach")
     }

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         Log.d(TAG, "$TAG onCreate")
     }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d(TAG, "$TAG onViewCreated")
        isViewPrepare = true
        initView()
        lazyLoadDataIfPrepared()
    }

     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         Log.d(TAG, "$TAG onActivityCreated")
     }

    private fun lazyLoadDataIfPrepared() {
        if (userVisibleHint && isViewPrepare && !hasLoadData) {
            lazyLoad()
            hasLoadData = true
        }
    }


    /**
     * 加载布局
     */
    @LayoutRes
    abstract fun getLayoutId():Int

    /**
     * 初始化 ViewI
     */
    abstract fun initView()

    /**
     * 懒加载
     */
    abstract fun lazyLoad()

}

我们可以将懒加载的逻辑放在BaseFragment中,就可以实现每个Fragment对用户可见时再去请求数据了。

至此,Fragment懒加载就实现了。

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值