Fragment 的懒加载

Fragment 懒加载是什么意思? 
所谓懒加载,即Fragment 的 UI 对用户可见时才加载数据。

以前我没啥经验,一股脑的从服务器拉取数据之后立马把数据绑定到 Fragment 的 UI 组件上,导致性能低下。后来知道了这一技术,才明白这才是移动端加载数据的正确姿势。

懒加载的技术关键点是什么? 
根据定义:所谓懒加载,即Fragment 的 UI 对用户可见时才加载数据。 
需要判断何时 Fragment 的 UI 才对用户可见

如何判断 Fragment 的 UI 是否对用户可见? 
Fragment 提供了一个方法 public void setUserVisibleHint(boolean isVisibleToUser),API 的注释如下

Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore
  • 1
  • 2

所以,只需要判断参数 isVisibleToUser 是否为 True 即可知道该 Fragment 的 UI 是否对用户可见。

setUserVisibleHint 在什么时候调用? 
对于单个 Fragment,setUserVisibleHint 是不会被调用的,只有该 Fragment 在 ViewPager 里才会被调用。所以,我写了一个 ViewPager + Fragment 的 Demo,打印了一下 Log。

D/Owen_TestFragment: setUserVisibleHint: isVisibleToUser = false
D/Owen_TestFragment: onAttach
D/Owen_TestFragment: onCreate
D/Owen_TestFragment: setUserVisibleHint: isVisibleToUser = true
D/Owen_TestFragment: onCreateView
D/Owen_TestFragment: onActivityCreated
D/Owen_TestFragment: onStart
D/Owen_TestFragment: onResume
D/Owen_TestFragment: onPause
D/Owen_TestFragment: onPause 
D/Owen_TestFragment: onStop
D/Owen_TestFragment: onDestroyView
D/Owen_TestFragment: onDestroy
D/Owen_TestFragment: onDetach
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以看到 setUserVisibleHint 的执行顺序是:

setUserVisibleHint(false) -> onAttach -> onCreate -> setUserVisibleHint(true)  -> onCreateView -> onActivityCreated ->.... -> onDetach
  • 1

代码 
为了方便,封装一个基类 LazyLoadFragment,提供一个 loadData() 方法供调用去加载数据

public abstract class LazyLoadFragment extends Fragment {

    /**
     * 控件是否初始化完成
     */
    private boolean isViewCreated;
    /**
     * 数据是否已加载完毕
     */
    private boolean isLoadDataCompleted;

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

    public abstract int getLayout();
    public abstract void initViews(View view);

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && isViewCreated && !isLoadDataCompleted) {
            isLoadDataCompleted = true;
            loadData();
        }
    }

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

        if (getUserVisibleHint()) {
            isLoadDataCompleted = true;
            loadData();
        }
    }

    /**
     * 子类实现加载数据的方法
     */
    public abstract  void loadData();
}
  • 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

等等,为什么 loadData() 会在两个地方执行?在 setUserVisibleHint 方法里执行我还能理解,为什么 onActivityCreated 也要执行呢?

因为,ViewPager 默认显示第一页,第一页肯定要先加载数据啊,而且 setUserVisibleHint 的执行顺序又是在 onCreatView 之前,同时 onCreatView 需要初始化界面和修改 isViewCreated 的值。所以就需要在 onActivityCreated 里执行一次咯。

等等 
文章写到这里,我听到了一个不同的声音

ViewPager 不是有 setOffscreenPageLimit(int limit) 方法吗?我调用 viewPager.setOffscreenPageLimit(0) 不就行了吗? 
我想说:思路是对的,但是这样做没效果。为什么?看一下 setOffscreenPageLimit 的方法实现就知道了

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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

limit 默认为 1 ,就算传一个 0 也无济于事啊。

总结 
懒加载的技术关键点 
setUserVisibleHint 的执行顺序 
为什么 ViewPager.setOffscreenPageLimit(0) 无效? 
参考来源 
tablayout+viewpager+fragment组合使用以及懒加载机制http://yaohepeng.com/2015/10/09/tablayout+viewpager+fragment组合使用以及懒加载机制/

 
 
posted @ 2016-12-28 11:02  天涯海角路  阅读(159)  评论(0)    收藏  举报