ViewPager的缓存页面 与 预加载

2021-09-17  本文已影响0人  壹元伍角叁分

前言

  1. 缓存的目的是为什么?

    答:为了预加载。

  2. 什么是预加载?

    答:Fragment切换的时候,会预先加载未可见的Fragment,就是预加载。

  3. 在缓存页面setOffscreenPageLimit(int limit) 是什么?

    答:可以设置缓存页面,设置3就是缓存6个Fragment,设置4就是缓存8个Fragment。

  4. 为什么这个函数设置 0 无效,为什么缓存的页面数不能低于1?

    答:setOffscreenPageLimit 看源码分析,因为就算是设置0,默认内部也会被修改成1。

  5. 在预加载 - setOffscreenPageLimit(int limit) 是什么?

    答:可以设置预加载,设置2就是预加载T2、T3。默认打开T1,预加载T2、T3;默认打开T2,预加载T3、T4

  6. 预加载会带来什么问题?怎么解决?

    答:会带来问题:1.预加载的越多就会越卡;2.一个Fragment占用 1M,5个就(5*1M),累计到后面就会OOM;3.如果预加载的Fragment在请求网络,不仅浪费流量,还会很卡顿.......解决办法:使用懒加载,来解决预加载带来的问题。

  7. 懒加载 是什么?

    答:防止预加载,用到才加载,可见才加载,不可见就不加载。
    懒加载,其实也就是延迟加载,就是等到该页面的UI展示给用户时,再加载该页面的数据(从网络、数据库等),而不是依靠 ViewPager预加载机制提前加载两三个,甚至更多页面的数据。
    目的:这样可以提高所属Activity的初始化速度,也可以为用户节省流量。而这种懒加载的方式也已经正在被诸多APP所采用。

源码解析

ViewPager.onMeasure(int widthMeasureSpec, int heightMeasureSpec)
//这个是最重要的步骤
--> populate();
    --> populate(mCurItem);
        //准备适配
        --> mAdapter.startUpdate(this);
            -->  startUpdate((View) container);
        --> curItem = addNewItem(mCurItem, curIndex);
            //创建适配的item数据
            --> ii.object = mAdapter.instantiateItem(this, position);
                --> fragment.setMenuVisibility(false);@FragmentPagerAdapter
                --> fragment.setUserVisibleHint(false);
        //销毁适配的item数据
        --> mAdapter.destroyItem(this, pos, ii.object);
            --> mCurTransaction = mFragmentManager.beginTransaction();@FragmentPagerAdapter
        //设置当前显示的item数据
        --> mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
            -->  if (fragment != mCurrentPrimaryItem) {
                     if (mCurrentPrimaryItem != null) {
                         //重置上一个item的属性
                         mCurrentPrimaryItem.setMenuVisibility(false);
                         mCurrentPrimaryItem.setUserVisibleHint(false);
                     }
                     //设置当前item的属性
                     fragment.setMenuVisibility(true);
                     fragment.setUserVisibleHint(true);
                     mCurrentPrimaryItem = fragment;
                 }
        //完成适配。接着才开始执行fragment的生命周期
        --> mAdapter.finishUpdate(this);
            --> mCurTransaction.commitNowAllowingStateLoss();
setOffscreenPageLimit(int limit)
//DEFAULT_OFFSCREEN_PAGES = 1,如果limit<1,则设置为1。也就解释了为什么设置0无效。
--> 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(),具体分析同上
       populate();
    }

流程分析

1、初次进入:

scom.xh.myviewpagerfragmentapp D/BlankFragment: etUserVisibleHint: tab=1,isVisibleToUser=false
com.xh.myviewpagerfragmentapp D/BlankFragment: setUserVisibleHint: tab=2,isVisibleToUser=false
com.xh.myviewpagerfragmentapp D/BlankFragment: setUserVisibleHint: tab=1,isVisibleToUser=true
com.xh.myviewpagerfragmentapp D/BlankFragment: onAttach: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreate: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onAttach: tab=2
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreate: tab=2
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreateView: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onViewCreated: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onActivityCreated: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onStart: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onResume: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreateView: tab=2
com.xh.myviewpagerfragmentapp D/BlankFragment: onViewCreated: tab=2
com.xh.myviewpagerfragmentapp D/BlankFragment: onActivityCreated: tab=2
com.xh.myviewpagerfragmentapp D/BlankFragment: onStart: tab=2
com.xh.myviewpagerfragmentapp D/BlankFragment: onResume: tab=2

2、从tab1点击进入tab3:

//下面两个是由mAdapter.instantiateItem(this, position)函数触发;
setUserVisibleHint: tab=3,isVisibleToUser=false //加载tab3
setUserVisibleHint: tab=4,isVisibleToUser=false //预加载tab4
//下面两个是由mAdapter.setPrimaryItem(this, mCurItem, curItem.object);函数触发;
setUserVisibleHint: tab=1,isVisibleToUser=false //重置tab1
setUserVisibleHint: tab=3,isVisibleToUser=true  //显示tab3
//下面才开始执行fragment的生命周期函数,由mAdapter.finishUpdate(this)触发
com.xh.myviewpagerfragmentapp D/BlankFragment: onAttach: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreate: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onAttach: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreate: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreateView: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onViewCreated: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onActivityCreated: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onStart: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onResume: tab=3
com.xh.myviewpagerfragmentapp D/BlankFragment: onCreateView: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onViewCreated: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onActivityCreated: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onStart: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onResume: tab=4
com.xh.myviewpagerfragmentapp D/BlankFragment: onPause: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onStop: tab=1
com.xh.myviewpagerfragmentapp D/BlankFragment: onDestroyView: tab=1

使用懒加载流程:

1、在setUserVisibleHint(boolean isVisibleToUser),判断参数isVisibleToUser为true时才去加载数据,false则停止加载

 override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
    if (isVisibleToUser) {
         startDoThings()
    } else {
         stopDoThings()
        }
    }

2、解决崩溃:由于setUserVisibleHint(boolean isVisibleToUser)在onCreateView()之前执行,有可能view还未被创建。所以需要设置一个tag,在onCreateView()中置为true。
然后在tag为true时才去加载/停止加载数据

override fun onCreateView(inflater: LayoutInflater, container:ViewGroup?,
        savedInstanceState: Bundle?): View? {
    ...
    viewCreate = true
    ...
}

override fun onDestroyView() {
    super.onDestroyView()
    ...
    viewCreate = false
    ...
}

 override fun setUserVisibleHint(isVisibleToUser: Boolean) {
    super.setUserVisibleHint(isVisibleToUser) 
    if (viewCreate) {
       if (isVisibleToUser) {
          startDoThings()
       } else {
          stopDoThings()
       }
    }
}

3、解决第一次进入不加载:第一次进入,setUserVisibleHint(boolean isVisibleToUser)在onCreateView()之前执行,加载数据的逻辑并未执行,
所以需要我们在onCreateView()中手动执行加载逻辑,在getUserVisibleHint()为true的时候执行。

override fun onCreateView(inflater: LayoutInflater, container:ViewGroup?,savedInstanceState: Bundle?): View? {
     ...
     viewCreate = true
     if (userVisibleHint) {
         userVisibleHint = true
     }
     ...
}

总结

Fragment 生命周期按先后顺序:
onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume ->
onPause -> onStop -> onDestroyView -> onDestroy -> onDetach

ViewPager + Fragment 的实现我们需要关注的几个生命周期有: onCreatedView + onResume + onPause + onDestroyView非生命周期函数:setUserVisibleHint + onHiddenChanged

上一篇 下一篇

猜你喜欢

热点阅读