懒加载BaseLazyFragment原理及代码

懒加载的目的主要是应用于在装有多个Fragment并进行切换的时候,如viewPager。相信各位在写项目的时候,肯定也有遇到过viewpager里的fragment进行了前后预加载,即3个fragment的数据加载,要通过setOffscreenPageLimit来设置预加载的项目,不设置setOffscreenPageLimit,则默认为1(设置0无效,可以查看该方法源码知道),即时设置了让他预加载为0了,也会执行多个预加载。这个很让人头疼,即大大降低了性能,又浪费初始化资源。然而我们采用懒加载技术就能让用户看到的页面才会加载他的数据,大大提高效率。
懒加载的实现原理就是利用Fragment中的setUserVisibleHint()和onCreateView(),主要是利用了setUserVisibleHint()对fragment的可见状态监测和在oncreateView()前执行的优点。注意加载时,如果这个fragment不可见的加载,那setUserVisibleHint()没有执行。
当viewPager中fragment改变可见状态, 使用getUserVisibleHint() 可以返回fragment是否可见状态:从可见到不见,为false;从不可见切换到可见,为true。
可见如图:具体参考博客****http://blog.csdn.net/mr_immortalz/article/details/51015196

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

注意:滑动到第三个fragment时,第一个fragment移除了相关的视图但未和活动解除关联。所以从3滑回2时,预加载的1的生命周期少一些执行。

下面我们来看懒加载BaseLazyFragment的class代码:
1、首先看setUserVisibleHint()方法的重写。


第一个判断语句主要是两个判断条件
1)当isVisibleToUser 为true则进行数据加载,当isVisibleToUser为false则不进行数据加载2)判断isInit值,对于已经加载过数据的fragment,再次被滑动到也不在进行加载数据,也就是每个fragment仅做一次数据加载工作第二个判断语句是针对以初始化的fragment,再根据是否可见来判断显示、执行生命周期。
2、然后回到onCreateView()上。在onCreateView()里,对要懒加载的fragment是添加FrameLayout。

如我注释所写,应该比较好理解的吧?(一开始我看了几篇文章还是很混,直到看了(http://blog.csdn.net/mr_immortalz/article/details/51015196的生命周期演示后,就大体上有个了解了,各位可以看看。)
3、然后是对fragment生命周期的处理,每个状态下以Fragment的回调方法名字后面要加个Lazy的方法名命名,并依此调用。
注意:onFragmentStopLazy()、onFragmentStartLazy()、onResumeLazy()在其他地方也有调用的。

4、加载view,我们在onCreateView()里,对要懒加载的fragment是,添加FrameLayout,设置setContentView()是为了当我们将fragment移至可见,则能够根据判断,替换掉原来的FrameLayout布局。因为,移至可见时,该fragment会再次调用onCreateView()方法嘛。

最后,我贴一下我的代码,代码开头注释里讲了整体的怎么懒加载思考思路。

package android.davidnba.com.davidnba_ywh.base;

import android.davidnba.com.davidnba_ywh.widget.LoadingDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

/* Created by 仁昌居士 on 2017/2/6.
 * 懒加载Fragment</h1> 只有创建并显示的时候才会调用onCreateViewLazy方法
 * 懒加载的原理onCreateView的时候Fragment有可能没有显示出来。
 * 但是调用到setUserVisibleHint(boolean isVisibleToUser),isVisibleToUser =
 * true的时候就说明有显示出来
 * 但是要考虑onCreateView和setUserVisibleHint的先后问题所以才有了下面的代码
 * 注意:
 * 《1》原先的Fragment的回调方法名字后面要加个Lazy,比如Fragment的onCreateView方法, 就写成onCreateViewLazy
 * 《2》使用该LazyFragment会导致多一层布局深度
 */


  /*  fragment一共四种情况:
         可见、初始化;
         可见、未初始化;
         不可见、初始化;
         不可见、未初始化;

         逻辑分析,假设setOffscreenPageLimit(1),共预加载2个fragment:
         刚进来,1是可见,且要初始化的,2是不可见、未初始化,第三个3是不绑定到activity的。
         于是,1走途径1,2走途径2.

         然后,假设滑动了,1滑到2。
           此时,1不可见,且已初始化了,2是可见,将要初始化,3绑定到activity上,为不可见、未初始化。
           于是,1由于预加载的原因,未解除与视图的联系。所以onPause()、onStop()、onDestoryView()未执行,
           并且,由于1不可见,setUserVisibleHint()执行了。所以, isStart设置为false,即不可见;并调用onFragmentStopLazy();
           2可见,同理由于预加载原因,处于可见状态时,不会再调用onCreateView(),而是会调用setUserVisibleHint(),
           所以,isStart设置为true;onFragmentStartLazy();
           3呢,调用onCreateView(),又由于3不可见且未初始化,则不执行。所以,加载的是个FrameLayout
         */
public class BaseLazyFragment extends BaseFragment {
    private boolean isInit = false;//真正要显示的View是否已经被初始化(正常加载)
    private Bundle savedInstanceState;
    public static final String INTENT_BOOLEAN_LAZYLOAD = "intent_boolean_lazyload";
    private boolean isLazyLoad = true;
    private FrameLayout layout;
    public LoadingDialog mLoadingDialog;
    private boolean isStart = false;//是否处于可见状态,in the screen

    @Override
    protected void onCreateView(Bundle saveInstanceState) {
        super.onCreateView(saveInstanceState);
        Bundle bundle = getArguments();
        if (bundle != null) {
            isLazyLoad = bundle.getBoolean(INTENT_BOOLEAN_LAZYLOAD, isLazyLoad);
        }
        //判断是否懒加载
        if (isLazyLoad) {
            //一旦isVisibleToUser==true即可对真正需要的显示内容进行加载
            if (getUserVisibleHint() && !isInit) {
                //途径1
                this.savedInstanceState = saveInstanceState;
                onCreateViewLazy(saveInstanceState);
                isInit = true;
            } else {
                //途径2
                //如果不可见或者已经初始化了
                //进行懒加载
                layout = new FrameLayout(getApplicationContext());
                layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                super.setContentView(layout);
            }
        } else {
            //途径3
            //这里是不可见,但已经初始化了
            //不需要懒加载,开门江山,调用onCreateViewLazy正常加载显示内容即可
            onCreateViewLazy(saveInstanceState);
            isInit = true;
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        //一旦isVisibleToUser==true即可进行对真正需要的显示内容的加载

        //判断fragment是否可见,是否还未被初始化,该fragment布局是否存在
        //可见,但还没被初始化,且该fragment布局存在
        if (isVisibleToUser && !isInit && getContentView() != null) {
            isInit = true; //允许初始化
            onCreateViewLazy(savedInstanceState);//调用懒加载
            onResumeLazy();
        }
        //对于已经加载过数据的fragment,再次被滑动到也不在进行加载数据,也就是每个fragment仅做一次数据加载工作
        //如果已经初始化了,且该fragment布局存在
        if (isInit && getContentView() != null) {
            if (isVisibleToUser) {
                isStart = true;
                onFragmentStartLazy();
            } else {
                isStart = false;
                onFragmentStopLazy();
            }
        }
    }


    protected void onCreateViewLazy(Bundle savedInstanceState) {
        Log.d("TAG", "onCreateViewLazy() called with: " + "savedInstanceState = [" + savedInstanceState + "]");
    }

    //当Fragment被滑到不可见的位置时,调用
    protected void onFragmentStopLazy() {
        Log.d("TAG", "onFragmentStopLazy() called with: " + "");
    }

    //当Fragment被滑到可见的位置时,调用
    protected void onFragmentStartLazy() {
        Log.d("TAG", "onFragmentStartLazy() called with: " + "");

    }

    protected void onResumeLazy() {
        Log.d("TAG", "onResumeLazy() called with: " + "");
    }

    protected void onPauseLazy() {
        Log.d("TAG", "onPauseLazy() called with: " + "");
    }

    protected void onDestroyViewLazy() {

    }


    @Deprecated
    @Override
    public final void onStart() {
        Log.d("TAG", "onStart() : " + "getUserVisibleHint():" + getUserVisibleHint());
        super.onStart();
        if (isInit && !isStart && getUserVisibleHint()) {
            isStart = true;
            onFragmentStartLazy();
        }
    }

    @Deprecated
    @Override
    public final void onStop() {
        super.onStop();
        Log.d("TAG", "onStop() called: " + "getUserVisibleHint():" + getUserVisibleHint());
        if (isInit && isStart && getUserVisibleHint()) {
            isStart = false;
            onFragmentStopLazy();
        }
    }

    @Override
    @Deprecated
    public final void onResume() {
        Log.d("TAG", "onResume() : " + "getUserVisibleHint():" + getUserVisibleHint());
        super.onResume();
        if (isInit) {
            onResumeLazy();
        }
    }

    @Override
    @Deprecated
    public final void onPause() {
        Log.d("TAG", "onPause() : " + "getUserVisibleHint():" + getUserVisibleHint());
        super.onPause();
        if (isInit) {
            onPauseLazy();
        }
    }

    @Override
    @Deprecated
    public final void onDestroyView() {
        Log.d("TAG", "onDestroyView() : " + "getUserVisibleHint():" + getUserVisibleHint());
        super.onDestroyView();
        if (isInit) {
            onDestroyViewLazy();
        }
        isInit = false;
    }

    @Override
    public void setContentView(int layoutResID) {
        //判断若isLazyLoad==true,移除所有lazy view,加载真正要显示的view
        if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {
            layout.removeAllViews();
            View view = inflater.inflate(layoutResID, layout, false);
            layout.addView(view);
        }
        //否则,开门见山,直接加载真正要显示的view
        else {
            super.setContentView(layoutResID);
        }
    }

    @Override
    public void setContentView(View view) {
        //判断若isLazyLoad==true,移除所有lazy view,加载真正要显示的view
        if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {
            layout.removeAllViews();
            layout.addView(view);
        }
        //否则,开门见山,直接加载真正要显示的view
        else {
            super.setContentView(view);
        }
    }
}

参考博客:
[http://blog.csdn.net/mr_immortalz/article/details/51015196]
http://blog.csdn.net/sinat_15877283/article/details/51037987
http://www.jianshu.com/p/58fd611260b5
http://www.open-open.com/lib/view/open1477033083443.html

推荐阅读更多精彩内容