懒加载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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265

推荐阅读更多精彩内容