安卓性能优化之懒加载的原理及实现(超详细仿微信头条实现Fragment数据懒加载)

从ViewPager的源码层理解实现fragment 的懒加载

  • Part1:懒加载概述
      • 1: 懒加载的由来和预加载的弊端
      • 2:什么是懒加载?
      • 3:从ViewPager的源码理解为什么不能通过setOffscreenPageLimit方法关闭ViewPager的预加载?
          • 1:ViewPager的 setOffscreenPageLimit()方法
          • 2:ViewPager的 populate()方法
          • 3:populate(即缓存)方法总结一下:
  • Part2:懒加载的详细实现与封装
      • 1:懒加载的整体设计思想
      • 2:fragment哪些方法与懒加载有关
      • 3:懒加载LazyFragment的详细代码实现和封装
          • step1:setUserVisibleHint方法的逻辑处理
          • step2:onCreateView函数的实现
          • step3: onPause方法的实现
          • step4: onResume方法的实现
          • step5: onDestroyView方法的实现
          • step6:onHiddenChanged方法的实现
      • 4:懒加载之进阶`ViewPager+Fragment的`嵌套使用时子ViewPager的Frangment如何实现懒加载
      • 5:最后附上封装好的`LazyFragment`的完整代码和下载地址:

阅读须知

  1. 本问Part1主要是从ViewPage的源码去分析预加载机制的实现原理和产生的问题,从而引出懒加载,
  2. Part2主要是从Fragment的六个方法入手,讲述懒加载的实现原理和步骤,以及ViewPager+Fragment嵌套使用的时候子ViewPager中Fragment的懒加载实现
  3. 本文part1和part2没有特别直接的关联,如果不关新ViewPager预加载原理的同学,可以直接跳过Part1
  4. 附上项目的下载地址以供参考:Demo下载地址

Part1:懒加载概述

1: 懒加载的由来和预加载的弊端

在Android开发过程中fragment通常会和ViewPager结合使用,通过Viewpager的预加载机制,提升App的体验效果,但是这样把我们看不到的页面的数据也加载了,初始化时浪费资源大大降低了性能,容易造成卡顿现象,尤其是在页面请求数据比较多、网络比较慢时它的缺点也就暴露了越来越明显。懒加载的出现就是为了解决以上Viewpager的预加载的弊端。

2:什么是懒加载?

懒加载通俗点讲一句话:当用户可以看见的时候才去进行网络数据的加载。懒加载的作用就是解决ViewPager和fragment结合使用时的弊端。目前一些大厂APP的首页都是通过懒加载实现,如微信、今日头条等。

从技术层面上讲,懒加载的原理是通过Fragment的生命周期中的几个方法 和非生命周期setUserVisibleHint以及onHiddenChanged方法结合使用,实现关闭ViewPager的预加载机制,从而实现懒加载。

3:从ViewPager的源码理解为什么不能通过setOffscreenPageLimit方法关闭ViewPager的预加载?

既然通过ViewPager的setOffscreenPageLimit(int i)实现的预加载,那么能不能通过setOffscreenPageLimit(0)去避免,预加载从而实现懒加载呢?当然是不能了,因为在ViewPagersetOffscreenPageLimit方法中对参数做了校验,当小于1时传入默认值1,无法关闭预加载从而实现懒加载。

接下看看和ViewPager预加载相关的几个方法的源码去理解一下,ViewPager如何实现的预加载以及setOffscreenPageLimit方法中参数为0时为什么不能关闭预加载机制。

1:ViewPager的 setOffscreenPageLimit()方法

ViewPager的 setOffscreenPageLimit()方法分析:主要是用来控制缓存fragment的数量,这里容易有个误区:Fragment本身是不具有缓存功能,只有在ViewPager+Fragment+PagerAdpater结合使用时,通过ViewPager实现的fragment的缓存 。

/**
     * 作用:控制缓存Fragment的数量, 默认缓存为1,
     *      因为当参数limit小于1时,赋值为1,所以默认缓存一帧
     *      这也就是设置为0是,无法关闭预加载的的原因
     * @param limit
     */
    public void setOffscreenPageLimit(int limit) {
        if (limit < 1) {
            Log.w("ViewPager", "Requested offscreen page limit " + limit + " too small; defaulting to " + 1);
           //当小于1时赋值为1,所以默认缓存一帧
            limit = 1;
        }
        if (limit != this.mOffscreenPageLimit) {
          //记录limit
            this.mOffscreenPageLimit = limit;
            //调用populate方法,进行布局排列
            this.populate();
        }
    }
2:ViewPager的 populate()方法

这里populate方法是 ViewPager 一个核心方法,非常重要是缓存功能实现:缓存功能是 populate方法在不同时机调用PagerAdapter的相关方法实现,缓存的原理是存放到一个ArrayList集合中,这里只是简过一下PagerAdapter或FragmentPagerAdapter的执行流程,详细源码,日后将专门写一篇关于ViewPager的源码分析,这里主要分析一下和预加载实现相关的源码:

源码的分析过程请看下面注释中的:Step1-Step5
    void populate() {
      //调用重载方法
        this.populate(this.mCurItem);
    }
    void populate(int newCurrentItem) {
        //用于存储缓存的内容的实体,这里主要是存放Fragment
        ViewPager.ItemInfo oldCurInfo = null;
        if (this.mCurItem != newCurrentItem) {
            //当不同时,取出旧的Fragment
            oldCurInfo = this.infoForPosition(this.mCurItem);
            //更新缓存数量
            this.mCurItem = newCurrentItem;
        }

        if (this.mAdapter == null) {
            this.sortChildDrawingOrder();
        } else if (this.mPopulatePending) {
            this.sortChildDrawingOrder();
        } else if (this.getWindowToken() != null) {
            /**
             * Step1:mAdapter.startUpdate(this),PagerAdapter 的startUpdate方法
             *   开始更新
             */
            this.mAdapter.startUpdate(this);
            int pageLimit = this.mOffscreenPageLimit;
            int startPos = Math.max(0, this.mCurItem - pageLimit);
            /**
             * Step2:调用PagerAdapter的getCount方法获取fragment的总数
             *     确定缓存空间[startPos,endPos]即[ this.mCurItem - pageLimit,this.mCurItem + pageLimit]
             */
            int N = this.mAdapter.getCount();
            int endPos = Math.min(N - 1, this.mCurItem + pageLimit);
            if (N != this.mExpectedAdapterCount) {
                String resName;
                try {
                    resName = this.getResources().getResourceName(this.getId());
                } catch (NotFoundException var17) {
                    resName = Integer.toHexString(this.getId());
                }

                throw new IllegalStateException("The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: " + this.mExpectedAdapterCount + ", found: " + N + " Pager id: " + resName + " Pager class: " + this.getClass() + " Problematic adapter: " + this.mAdapter.getClass());
            } else {
                int curIndex = true;
                ViewPager.ItemInfo curItem = null;

                int curIndex;
                for(curIndex = 0; curIndex < this.mItems.size(); ++curIndex) {
                    ViewPager.ItemInfo ii = (ViewPager.ItemInfo)this.mItems.get(curIndex);
                    if (ii.position >= this.mCurItem) {
                        if (ii.position == this.mCurItem) {
                            curItem = ii;
                        }
                        break;
                    }
                }

                /**
                 * Step3:如果没有找到curItem,即没有缓存fragment,则调用addNewItm加载一个 addNewItem中:
                 *     1: ii.object = this.mAdapter.instantiateItem(this, position);
                 *         即通过PagerAdapter的instantiateItem方法创建一个fragment并返回,存放到li(ViewPager.ItemInfo li)
                 *         的object属性中,此过程需要看子类FragmentPagerAdapter中对应的方法,其源码不在这里描述
                 *     2: 然后先把把li放到一个ArrayList的集合中,最后将li返回
                 *
                 *     3:缓存的fragment的生命周期就从此开始
                 */
                if (curItem == null && N > 0) {
                    curItem = this.addNewItem(this.mCurItem, curIndex);
                }

                int itemIndex;
                ViewPager.ItemInfo ii;
                int i;
                if (curItem != null) {
                    //左边item进行缓存处理
                    float extraWidthLeft = 0.0F;
                    itemIndex = curIndex - 1;
                    ...省略部分代码
                    for(int pos = this.mCurItem - 1; pos >= 0; --pos) {
                        if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                             ...省略部分代码
                            if (pos == ii.position && !ii.scrolling) {
                                this.mItems.remove(itemIndex);
                                /**
                                 * Step4:当item不在缓存范围内时,调用mAdapter.destroyItem销毁
                                 */
                                this.mAdapter.destroyItem(this, pos, ii.object);
                                --itemIndex;
                                --curIndex;
                                ii = itemIndex >= 0 ? (ViewPager.ItemInfo)this.mItems.get(itemIndex) : null;
                            }
                            ...省略部分代码
                    }

                    float extraWidthRight = curItem.widthFactor;
                    itemIndex = curIndex + 1;
                    //处理右边的缓存
                    if (extraWidthRight < 2.0F) {
                        ii = itemIndex < this.mItems.size() ? (ViewPager.ItemInfo)this.mItems.get(itemIndex) : null;
                        float rightWidthNeeded = i <= 0 ? 0.0F : (float)this.getPaddingRight() / (float)i + 2.0F;
                        for(int pos = this.mCurItem + 1; pos < N; ++pos) {
                            if (extraWidthRight >= rightWidthNeeded && pos > endPos) {
                                ...省略部分代码
                                if (pos == ii.position && !ii.scrolling) {
                                    this.mItems.remove(itemIndex);
                                    /**
                                     * Step4:当item不在缓存范围内时,调用mAdapter.destroyItem销毁
                                     */
                                    this.mAdapter.destroyItem(this, pos, ii.object);
                                    ii = itemIndex < this.mItems.size() ? (ViewPager.ItemInfo)this.mItems.get(itemIndex) : null;
                                }
                            } else if (ii != null && pos == ii.position) {
                               ...省略部分代码
                            } else {
                                 ...省略部分代码
                            }
                        }
                    }
                    this.calculatePageOffsets(curItem, curIndex, oldCurInfo);
                    this.mAdapter.setPrimaryItem(this, this.mCurItem, curItem.object);
                }
                    /**
                     * Step5:调用PagerAdapter的finishUpdate方法完成更新
                     */
                this.mAdapter.finishUpdate(this);
                //。。。省略部分代码,主要是对子fragment的缓存处理
                int childCount = this.getChildCount();
               ......代码
        }
    }
}

3:populate(即缓存)方法总结一下:
  1. 执行完populate方法后至少缓存1帧的数据
  2. ViewPage的缓存功能是通过将缓存的frament存放到ArrayList中。
  3. 预加载的设计是通过PagerAdapter穿插管理
  4. 这种缓存就是我们所说的预加载,预加载会造成浪费资源、耗内存、卡顿等一系列的问题。

Part2:懒加载的详细实现与封装

1:懒加载的整体设计思想

懒加载就是不让Viewpager进行预加载数据,即当用户肉眼可见时才去进行数据加载。
懒加载的流程将围绕Fragment可见状态实现分为以下三个种情况:

  1. 首先要区分Fragment第一次可见,因为Fragment的第一次和非第一次可见分别对应View的创建和View的更新两种情况,因为在非第一次可见时,大部分的信息都是从缓存中获取的。
  2. Fragment每次对用户可见时,才需要网络请求加载数据
  3. Fragment每次对用户不可见时,如果有线程加载数据,需要中断网络请求

2:fragment哪些方法与懒加载有关

懒加载和Fragment的声明周期有关的几个方法:

  1. onCreateView:初始化Fragment的布局,不建议在此处执行耗时操作
  2. onResume和onPause:此时视图是可见的即前台可见,只是onResume时可以和用户交互,onPause时不能和用户交互。注意区分这里的可见和和上面说到的对用户可见是两种情况不要混淆了
  3. onDestroyView: 销毁和Fragment相关的视图,但是此时未和Activity解绑,此时仍然可以通过onCreateView创建视图。

懒加载和Fragment的生命周期无关的方法:

  1. setUserVisibleHint方法,此方法会在onCreateView()之前执行,当viewPager中fragment改变可见状态时也会调用,使用getUserVisibleHint() 可以返回fragment是否可见状态,如果可见则进行懒加载操作
  2. onHiddenChanged方法,在使用FragmentTransactionhiddenshow方法管理fragment时,是不会走Fragment正常的生命周期,而是走onHiddenChanged方法,此方法可以监听Fragment 的显示与隐藏。

在以上六个方法中分别作对应的逻辑处理才能真正实现懒加载。

3:懒加载LazyFragment的详细代码实现和封装

在讲代码之前理解三个概念,以便理解懒加载的实现过程,当前Fragment、目标Fragment和缓存Fragment。
例如:页面有三个Fragment:Fragment1、Fragment2 、Fragment3,当用户在Fragment1上进行操作,打算跳到Fragment2中,则三个角色分别为

  1. 当前Fragment:当前用户操作的fragment,即Fragment1。
  2. 目标Fragment:要切换的fragment,即Fragment2。
  3. 缓存Fragment:预加载的Fragment,即Fragment3。

首先看定义的三个成员变量:

 boolean mIsViewCreated=false;//Fragment是否已经创建,
 boolean mIsFirstVisiable=true;//Fragment是否第一次可见
 boolean mCurrentVisiableState=false;//标记保存Fragment的可见状态,表示当前Fragment是否分发过,  可见到不可见  不可见到可见才可以调用 disPatchVisibaleHint  防止重复调用
step1:setUserVisibleHint方法的逻辑处理

因为setUserVisibleHint方法是最先执行的在此先看他的实现

 //与生命周期无关的函数
    /**
     * onCreateView在此方法之后  第一次时 mIsViewCreated=false
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        //用户是否可见
       Log.e(TAG,"setUserVisibleHint----"+isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
        /**
         * 1:分发的前提是FragmentView已经创建,因为setUserVisibleHint在fragment 的声明周期之前执行,
         * 如果页面没创建就去分发容易造成空指针异常
         *
         * 2:因为此方法是由setUserVisibleHint方法调用,而setUserVisibleHint方法由系统的Viewpager调用多次,我们没办法控制它,
         *    但是我们可以控制disPatchVisibaleHint的分发
         */
        if (mIsViewCreated){
            //只有Fragment创建了后才进行分发
            if (isVisibleToUser&& !mCurrentVisiableState){//防止重复调用disPatchVisibaleHint !mCurrentVisiableState表示没有分发过时才分发
                //用户可见时进行分发事件
                disPatchVisibaleHint(true);
            }else if (mCurrentVisiableState&& !isVisibleToUser){
                //用户不可见时不分发事件
                disPatchVisibaleHint(false);
            }
        }
    }

 /**
     * 分发可见
     * 调用的前提:可见--》不可见    或者不可见--》可见
     * @param isVisiable
     */
    private void disPatchVisibaleHint(boolean isVisiable) {
        Log.e(TAG,"disPatchVisibaleHint----"+isVisiable);

        if (mCurrentVisiableState==isVisiable){
            //防止调用两次更新,存在当 mCurrentVisiableState=isVisiable;执行前setUserVisibleHint可能被调用两次
            return;
        }
        mCurrentVisiableState=isVisiable;//进行赋值操作
        if (isVisiable){
            //可见
            if (mIsFirstVisiable){
                mIsFirstVisiable=false;
                //处理第一次可见时
                //公共方法,由子类实现
                onFragmentFirstVisiable();
            }
            //复写onFragmentResume分发事件,网路请求
            onFragmentResume();
            //对viewpager嵌套使用时处理子的fragment
            dispatChChildVisiableState(true);
        }else {
        	//复写onFragmentPause终止数据请求
            onFragmentPause();
            //对viewpager嵌套使用时处理子的fragment
            dispatChChildVisiableState(false);
        }
    }
        /**
     *  下面抽取三个方法定义成public方法是因为:
     *  父类不知道子类要不要处理可见和不可见,因为子类有可能不需要懒加载,此时子类什么也不需要做
     *  当需要懒加载时,才需要复写这三个方法。
     *  因此这3个方法是非必须实现的方法,不能定义成抽象方法
     */

    /**
     * 第一次可见时特殊处理
     */
    public void onFragmentFirstVisiable() {
        Log.e(TAG,"onFragmentFirstVisiable");
    }

    /**
     * 不可见时处理相关动作  停止数据的加载
     */
    public void onFragmentPause() {
        Log.e(TAG,"onFragmentPause");
    }

    /**
     * 表面可见时  加载数据
     */
    public void onFragmentResume() {
        Log.e(TAG,"onFragmentResume");
    }

step2:onCreateView函数的实现
 @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        TAG=getClass().getSimpleName()+"----";
        Log.e(TAG,"onCreateView");
        //两个抽象方法,让子类实现initView和getLayoutRes
        if (mRootView==null) {
            mRootView=inflater.inflate(getLayoutRes(),null);
        }
        initView(mRootView);
         //1:创建了Fragment  控制下面分发的前提,因为分发事件由setUserVisibleHint方法控制,而setUserVisibleHint最先执行
        mIsViewCreated=true;

        //2:对于默认Fragment的加载,可以在此分发一下,可见才分发
        if (getUserVisibleHint()&&!isHidden()){
            //可见
            disPatchVisibaleHint(true);
        }
        return mRootView;
    }
step3: onPause方法的实现

这里需要描述一下场景,当从fragment跳转到另一个Activity时需要做的处理,
只需对当前用户可见的Fragment的数据加载进行终止

    @Override
    public void onPause() {
        Log.e(TAG,"onPause");
        if (mCurrentVisiableState&&getUserVisibleHint()){
            disPatchVisibaleHint(false );
        }
        super.onPause();
    }
step4: onResume方法的实现

这里需要描述一下场景,经过Step3后,又再次返回到Frangment所在的Activity时,只需要对当前用户可见的Fragment的数据加载

 @Override
    public void onResume() {
        Log.e(TAG,"onResume");
        //只有当前可见的Fragment  才更新shuju  点击home键又返回
        if (!mCurrentVisiableState&&getUserVisibleHint()&&!isHidden() ){
            disPatchVisibaleHint(true );
        }
        super.onResume();
    }
step5: onDestroyView方法的实现

主要是对控制变量进行恢复初始值操作##

 @Override
    public void onDestroyView() {
        Log.e(TAG,"onDestroyView");
        super.onDestroyView();
        //将所有的变量复位
        mIsFirstVisiable=true;
        mCurrentVisiableState=false;
        mIsViewCreated=false;
    }
step6:onHiddenChanged方法的实现
      @Override
    //与生命周期无关的函数
    //在使用`FragmentTransaction`的`hidden` 和`show`方法管理fragment时,是不会走Fragment正常的生命周期,
    // 而是走`onHiddenChanged`方法,此方法可以监听Fragment 的显示与隐藏。
    public void onHiddenChanged(boolean hidden) {
        Log.e(TAG,"onHiddenChanged");
//        FragmentTransaction管理Fragment时
        super.onHiddenChanged(hidden);
        if (hidden){
            disPatchVisibaleHint(false);
        }else {
            disPatchVisibaleHint(true);
        }
    }

到此与懒加载相关的六个方法的逻辑已经处理完毕,详细原理请看代码中的注解,再此不做过多赘述,在step1中的disPatchVisibaleHint方法中处理ViewPager嵌套使用时,对于子ViewPager中的Frangment处理交给了dispatChChildVisiableState方法,那这个处理过程是怎么什么样子的呢?请接着往下看。

4:懒加载之进阶ViewPager+Fragment的嵌套使用时子ViewPager的Frangment如何实现懒加载

先说原理:很简单,就是根据当父的Fragment对用户的可见状态,去处理子ViewPager中Fragment的事件分发。就是在上面disPatchVisibaleHint方法中
安卓性能优化之懒加载的原理及实现(超详细仿微信头条实现Fragment数据懒加载)_第1张图片
代码实现:

 /**
     * ViewPage嵌套使用时处理子的Fragment懒加载逻辑
     *  通过getChildFragmentManager();获取子Fragment
     *  @param visiable
     */
    private void dispatChChildVisiableState(boolean visiable){
        Log.e(TAG,"dispatChChildVisiableState ===== " + visiable);
        FragmentManager childFragmentManager = getChildFragmentManager();
        List childFragmentManagerFragments = childFragmentManager.getFragments();
        if (childFragmentManagerFragments != null) {
            for (Fragment fragment : childFragmentManagerFragments) {
                //进行类型校验,只有继承了LazyFragment才进行懒加载的处理
                if (fragment instanceof LazyFragment&&!fragment.isHidden()&&fragment.getUserVisibleHint()){
                  ((LazyFragment) fragment).disPatchVisibaleHint(visiable);
                }
            }
        }
    }

5:最后附上封装好的LazyFragment的完整代码和下载地址:

public abstract class LazyFragment extends Fragment {
    public  String TAG="---";

    View mRootView;
       boolean mIsViewCreated=false;//Fragment是否已经创建,
    boolean mIsFirstVisiable=true;//Fragment是否第一次可见
    boolean mCurrentVisiableState=false;//标记保存Fragment的可见状态,表示当前Fragment是否分发过  可见到不可见  不可见到可见才可以调用 disPatchVisibaleHint  防止重复调用

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        TAG=getClass().getSimpleName()+"----";
        Log.e(TAG,"onCreateView");
        //两个抽象方法,让子类实现initView和getLayoutRes
        if (mRootView==null) {
            mRootView=inflater.inflate(getLayoutRes(),null);
        }
        initView(mRootView);
        //1:创建了Fragment  控制下面分发的前提,因为分发事件由setUserVisibleHint方法控制,而setUserVisibleHint最先执行
        mIsViewCreated=true;

        //2:对于默认Fragment的加载,可以在此分发一下,可见才分发
        if (getUserVisibleHint()&&!isHidden()){
            //可见
            disPatchVisibaleHint(true);
        }
        return mRootView;
    }

    @Override
    public void onResume() {
        Log.e(TAG,"onResume");
        //只有当前可见的Fragment  才更新shuju  点击home键又返回
        if (!mCurrentVisiableState&&getUserVisibleHint()&&!isHidden() ){
            disPatchVisibaleHint(true );
        }
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.e(TAG,"onPause");
        if (mCurrentVisiableState&&getUserVisibleHint()){
            disPatchVisibaleHint(false );
        }
        super.onPause();
    }

    @Override
    public void onDestroyView() {
        Log.e(TAG,"onDestroyView");
        super.onDestroyView();
        //将所有的变量复位
        mIsFirstVisiable=true;
        mCurrentVisiableState=false;
        mIsViewCreated=false;
    }

    //与生命周期无关的函数
    /**
     * onCreateView在此方法之后  第一次时 mIsViewCreated=false
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        //用户是否可见
       Log.e(TAG,"setUserVisibleHint----"+isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
        /**
         * 1:分发的前提是FragmentView已经创建,因为setUserVisibleHint在fragment 的声明周期之前执行,
         * 如果页面没创建就去分发容易造成空指针异常
         *
         * 2:因为此方法是由setUserVisibleHint方法调用,而setUserVisibleHint方法由系统的Viewpager调用多次,我们没办法控制它,
         *    但是我们可以控制disPatchVisibaleHint的分发
         */
        if (mIsViewCreated){
            //只有Fragment创建了后才进行分发
            if (isVisibleToUser&& !mCurrentVisiableState){//防止重复调用disPatchVisibaleHint !mCurrentVisiableState表示没有分发过时才分发
                //用户可见时进行分发事件
                disPatchVisibaleHint(true);
            }else if (mCurrentVisiableState&& !isVisibleToUser){
                //用户不可见时不分发事件
                disPatchVisibaleHint(false);
            }
        }
    }

    @Override
    //与生命周期无关的函数
    //在使用`FragmentTransaction`的`hidden` 和`show`方法管理fragment时,是不会走Fragment正常的生命周期,
    // 而是走`onHiddenChanged`方法,此方法可以监听Fragment 的显示与隐藏。
    public void onHiddenChanged(boolean hidden) {
        Log.e(TAG,"onHiddenChanged");
//        FragmentTransaction管理Fragment时
        super.onHiddenChanged(hidden);
        if (hidden){
            disPatchVisibaleHint(false);
        }else {
            disPatchVisibaleHint(true);
        }

    }


    /**
     * 分发可见
     * 调用的前提:可见--》不可见    或者不可见--》可见
     * @param isVisiable
     */
    private void disPatchVisibaleHint(boolean isVisiable) {
        Log.e(TAG,"disPatchVisibaleHint----"+isVisiable);

        if (mCurrentVisiableState==isVisiable){
            //防止调用两次更新,存在当 mCurrentVisiableState=isVisiable;执行前setUserVisibleHint可能被调用两次
            return;
        }
        mCurrentVisiableState=isVisiable;//进行赋值操作
        if (isVisiable){
            //可见
            if (mIsFirstVisiable){
                mIsFirstVisiable=false;
                onFragmentFirstVisiable();
            }
            onFragmentResume();
            //处理子ViewPager嵌套使用时,子fragment懒加载的实现
            dispatChChildVisiableState(true);
        }else {
            onFragmentPause();
            dispatChChildVisiableState(false);
        }

    }

    /**
     *  下面抽取三个方法定义成public方法是因为:
     *  父类不知道子类要不要处理可见和不可见,因为子类有可能不需要懒加载,此时子类什么也不需要做
     *  当需要懒加载时,才需要复写这三个方法。
     *  因此这3个方法是非必须实现的方法,不能定义成抽象方法
     */

    /**
     * 第一次可见时特殊处理
     */
    public void onFragmentFirstVisiable() {
        Log.e(TAG,"onFragmentFirstVisiable");

    }

    /**
     * 不可见时处理相关动作  停止数据的加载
     */
    public void onFragmentPause() {
        Log.e(TAG,"onFragmentPause");

    }

    /**
     * 表面可见时  加载数据
     */
    public void onFragmentResume() {
        Log.e(TAG,"onFragmentResume");

    }

    /**
     * ViewPage嵌套使用时处理子的Fragment懒加载逻辑
     *  通过getChildFragmentManager();获取子Fragment
     *  @param visiable
     */
    private void dispatChChildVisiableState(boolean visiable){
        Log.e(TAG,"dispatChChildVisiableState ===== " + visiable);
        FragmentManager childFragmentManager = getChildFragmentManager();
        List childFragmentManagerFragments = childFragmentManager.getFragments();
        if (childFragmentManagerFragments != null) {
            for (Fragment fragment : childFragmentManagerFragments) {
                //进行类型校验,只有继承了LazyFragment才进行懒加载的处理
                if (fragment instanceof LazyFragment&&!fragment.isHidden()&&fragment.getUserVisibleHint()){
                  ((LazyFragment) fragment).disPatchVisibaleHint(visiable);
                }
            }
        }
    }
    protected abstract int getLayoutRes();
    protected abstract void initView(View rootView);
}

到此为止,懒加载原理和实现讲解结束结束,周末也到此结束,如果文章中有歧义的地方欢迎大家留言指出,共同进步。

你可能感兴趣的:(Android,原理及源码分析)