fragment延迟加载,多方法测试择优

废话不多说,fragment延迟加载,网上有好几种方法,想要求同存异有些耗费时间,如果你不想花时间研究写demo,就往下看吧。如果你想要花时间研究,也可以看一下前面几段就不用往下看了。

1、所谓的fragment延迟加载,目前为止按类型分有两种:

  • 1)一种是常见的APP主页,activity+fragments ,下面简称af
  • 2)一种是viewpager + fragments,下面简称vf

2、如果vf按类别分的话,还可以分:

  • 1) 在fragment内部调用延迟加载方法
  • 2)在fragment外部viewpager那层手动调用延迟加载方法

目前我就查到了这几种,如果还有请留言,感谢。


先上af代码:

**activity里面代码**

//hide 和 show才能触发onHidden add不能触发 
//所以要想做延迟加载 在onHidden里面处理的话 需要初始化的时候就把所有的fragment都add 
//但是 add了还不行  必须 要commit 一次,否则 如果放在同一个commit执行
//那么 onCreate 之后就会立马执行onHidden而此时还咩有onCreateView所以会报空指针
   private void initFragments() {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        if (fragmentList.size() == 0) {
            for (int i = 0; i < 3; i++) {
                MainItemFragment fragment = new MainItemFragment();
                Bundle bundle = new Bundle();
                bundle.putInt("type", i + 1);
                fragment.setArguments(bundle);
                fragmentList.add(fragment);
                fragmentTransaction.add(R.id.container, fragment, "fragment" + i);
                Log.d("debug", ":add :" + i);
            }
        }
        fragmentTransaction.commitAllowingStateLoss();
    }

  private void changeFragment(int index) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
//      如果不等于空就初始化
//        hide 和 show才能触发onHidden add不能触发 所以要想做延迟加载
//       在onHidden里面处理的话 需要初始化的时候就把所有的fragment都add
        hideAll(fragmentTransaction);
        fragmentTransaction.show(fragmentList.get(index));
        fragmentTransaction.commitAllowingStateLoss();
    }

    private void hideAll(FragmentTransaction fragmentTransaction) {
        for (int i = 0; i < fragmentList.size(); i++) {
            if (fragmentList.get(i) != null) {
                fragmentTransaction.hide(fragmentList.get(i));
            }
        }
    }
**fragment里面代码**

 @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            delayInit();
        }
        Log.d("debug", type + ":onHiddenChanged" + hidden);
    }

 private boolean canLoad = true;

  public void delayInit() {
       if (!canLoad) {
            return;
       }
        //todo
  }

代码解释:上面的代码大家可以直接复制粘贴过去用,很方便,只需按需改动一下即可。
如果是求便捷的同学可以拿走了,没你事儿了,下面是测试过程,就是在写Demo的过程中的收获和需要注意的地方。

注意:

  • 1、我们的fragment里面 有onCreate,onCreateView,onActivityCreated 这几个主要方法,调用顺序也如此。而我们的加载view必须要放在 onCreateView之后,因为你看在onCreateView之后才有根布局,如果根布局都没有,那就会报空指针之类的错误。
  • 2、hide 和 show 才能触发onHidden,并且是在状态改变的时候。而 add不能触发 。

  • 3、需要两次commit ,一次是在init初始化的时候,需要把所有fragment add进去,然后commit一次,然后另一次是在changeFragment的时候commit.,并且,在show之前最好是把所有的fragment都hide掉(图方便,其实代码里面循环里做了判断的)

**log代码 **


这是第一次commit,执行add 1\2\3 验证一个道理:
**add 是不会让fragment执行 onCreate onCreatView 等一系列生命周期方法的 **
第二次commit, 执行了生命周期方法,验证一个道理:
只有当fragment show出来的时候才会执行其生命周期方法

回到上面那个问题,如果只有一次commit,我当初就是为了图方便,直接把initFragment写在changeFragment里面同一个transaction,结果:

    private void changeFragment(int index) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
//      如果不等于空就初始化
//        hide 和 show才能触发onHidden add不能触发 所以要想做延迟加载
//       在onHidden里面处理的话 需要初始化的时候就把所有的fragment都add
        if (fragmentList.size() == 0) {
            for (int i = 0; i < 3; i++) {
                MainItemFragment fragment = new MainItemFragment();
                Bundle bundle = new Bundle();
                bundle.putInt("type", i + 1);
                fragment.setArguments(bundle);
                fragmentList.add(fragment);
                fragmentTransaction.add(R.id.container, fragment, "fragment" + i);
                Log.d("debug", ":add :" + i);
            }
        }
        hideAll(fragmentTransaction);
        fragmentTransaction.show(fragmentList.get(index));
        fragmentTransaction.commitAllowingStateLoss();
    }
**如图,onHiddenChanged方法在onCreate之后 在onCreateView之前了**

这样的话,第一次是不会执行延迟加载方法的,进入是一片空白。只有所有的都add一遍了,然后再来切换,也就是commit add 所有fragment了一回,再commit change 才会执行延迟加载方法,呈现出布局。

OK以上的就是对于af这种类型的测试精简结果。


对于vf

鄙人拙见,所谓的延迟加载,如果在Viewpager里面的话,如果在不修改viewpager源码设置 defaultlimit = 0 的情况下,无论如何都会预加载1页,也就是每次都会最多加载2页的数据,也就是说 延迟加载 如果只有3个fragment 最多 可以节约1个fragment的数据内存, 如果是4个话可以节约2个,也就是n-2 个数据内存。(如果有不同看法的,快点留言!我渴望你的答案)

第一种:内部调用

 @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint()) {
            isVisible = true;
            onFragmentVisible();
        } else {
            isVisible = false;
            onFragmentInvisible();
        }
        Log.d("debug", type + ":setUserVisibleHint" + isVisibleToUser);
    }
//这里通过判断view是否隐藏或者显示状态来执行延迟加载方法
    private void onFragmentInvisible() {
        Log.d("debug", type + ":onFragmentInvisible" + isVisible);
    }

    private void onFragmentVisible() {
        Log.d("debug", type + ":onFragmentVisible" + isVisible);
        if (canLoad && isFirstLoad) {
            delayInit();

        }
    }


    public void delayInit() {
        isFirstLoad = false;
        textView = new TextView(mContext);
        textView.setText(type + "");

        ToggleButton button = new ToggleButton(mContext);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        handler.sendEmptyMessageDelayed(0, 2000);


        button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (!isChecked) {
                    setUserVisibleHint(isChecked);
                } else {
                    setUserVisibleHint(isChecked);
                }
            }
        });
        mRoot.addView(textView);
        mRoot.addView(button);
    }
 @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d("debug", type + ":onCreateView");
        if (mRoot == null) {
            mRoot = new LinearLayout(container.getContext());
        }
        if (isFirstLoad) {
            delayInit();
        }
        return mRoot;
    }

解释一下:上面有一些条件变量isFirstLoad、canLoad

  • isFirstLoad是判断是否是第一次加载,如果是第二次的话就不会再加载了。因为都已经加载过了。但是问题来了,viewpager如果不设置setOffscreenLimit(list.size() )的话,默认至少会预加载1页,那么,如果所有的3页都已经加载了,切换的时候会发生什么呢?

第三页切换第二页,第一页也依旧执行了onCreateVIew

所以,fragment除了onCreate只执行一次之外,onCreateView如果预加载的话也是再执行的。再具体一点:因为现在在第3页,会缓存第2页,但是第一页没有缓存已经destroyVIew了(因为limit == 1,如果是2那么第1页也缓存),而点击切换到第2页,有要默认预加载1页,第3页已经在缓存里面,而第1页没有,所以要执行onCreateView方法。
我们的isFirstLod就是为了避免多次加载

  • canLoad 这个就是比较重点的地方了。看上面的代码,canLoad默认是false,只有在onCreate里面才设置为true.为什么呢?(将引出一个问题!)
    我们来看看如果没有canLoad的话log:

注意: setUserVisibleHint 居然在onCreate前面执行了!!
这是我测试时,万万没有想到的,找了半天的源码看看哪里调用,终于找到:它在FragmentPageAdapter里面被调用


所以一定要小心了。canLoad就是为了解决这个问题,只有在onCreate(因为紧接着就是跟的onCreateView)或者onCreateView(最好这这里)之后才能去执行延迟加载方法

还有一点要注意:

 @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d("debug", type + ":onCreateView");
        if (mRoot == null) {
            mRoot = new LinearLayout(container.getContext());
        }
        if (isFirstLoad) {
            delayInit();
        }
        return mRoot;
    }

onCreateView里面,有一个对mRoot的判空,这里是可以判空的,因为已经执行过一次之后,就别再多次执行了,直接返回

第二种:外部调用
这种是群里朋友给我说的思路:给viewpager设置onPageChanged监听,onSelected里面 去调用延迟加载方法,并且把延迟加载方法public出来。

多的代码我就不上了,fragment里面差不多,其余代码也相差不大。有一点就是:默认的第一页你要在fragment里面去调用加载方法,其实也需要设置 isFirstload 、canload这些变量,实现也跟上面的方法差不多,就只是 调用方法一个在onselected里, 一个在setUserVisibleHint里。据我demo来看,没有啥区别和优势(如果,我说错了,有更好的方法,快点留言!我渴望你的答案,也希望学习到)

总结

研究了一下写了个demo,如果想要demo的话,那分享一下 github
吧。testdelayloadfragment这个module下的。demo里面很乱很杂,我不建议你pull下来。。。
最后,希望和热爱研究的小伙伴一起交流讨论!

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

推荐阅读更多精彩内容