懒加载---原理分析

懒加载的本意就是,让界面显示的时候再去加载数据。
对于Fragment来说,他的onCreateView()方法被执行了,界面才会出来。

ViewPager+Fragment模式

我们一般的做法是这样的

onCreate(){  
    mViewPager.setAdapter((new FragmentStatePagerAdapter(...));
    mViewPager.setCurrentItem(1);
}

在这里,要稍微的解释下,这两个方法究竟在ViewPager中做了什么。
刚学Android经常犯一个错误,就是在onCreate()方法中去拿某个View的宽高,比如,在这里mViewPager.getWidth()肯定为0,那么为什么为0呢?一般的解释是此时整个View树还没有完成测量、布局的操作。那么View树的第一次测量布局究竟是什么时候执行的呢?

为了解释我这个困惑,尝试着在onResume()中去拿到ViewPager的宽高,不出所料,肯定还是0。

View树的第一次测量布局

在ActivityThread的handleResumeActivity()中,在之前的onCreate()中,整个View树的数据已经创建出来,但是还没有显示出来,所以,在这里,执行一个IPC操作,将View树添加到WMS中,那么在客户端进程中的流程是ViewRootImpl的setView()--->requestLayout(),该方法发送一个scheduleTraversals()的异步任务,注意,也就是是说,当Activity的onResume()方法执行时,只是发送了一个异步的scheduleTraversals任务到UI队列中去,要等到下一次UI线程处理队列中的这个任务时,才会执行。所以,在onResume()中也无法拿到宽高。

setAdapter()方法

经过上面的分析,也就是说setAdapter()执行的时候,界面还没有出来。

public void setAdapter(...){
//......
            if(!wasFirstLayout) {      //是否是第一次布局,默认为true,当onLayout执行后赋值为false,所以这里会执行requestLayout()方法,发送一个异步布局消息。
                populate();
            } else {
                requestLayout();
            }
}

假设后面我们执行了setCurrentItem(1)方法,同样

public void setCurrentItem(){
       if (mFirstLayout) {      //此时为true
            // We don't have any idea how big we are yet and shouldn't have any pages either.
            // Just set things up and let the pending layout handle things.
            mCurItem = item;
          // ......
            requestLayout();
        } else {
            populate(item);
            scrollToItem(item, smoothScroll, velocity, dispatchSelected);
        }
}

这里会执行if中的代码,也就是该方法确定了mCurItem,也就是界面显示的时候究竟显示哪一个,此时是1。然后,也是发送了一个异步布局消息。

测量布局
onMeasure()方法

 // Make sure we have created all fragments that we need to have shown.
        mInLayout = true;
        populate();  //根据适配器提供的数据,创建出相关数据,便于后面的绘制
        mInLayout = false;

        // Page views next.
        size = getChildCount();    //当populate方法执行完后,子孩子已经被添加到ViewPager中去

setUserVisibleHint()什么时候执行

populate()--->addItem--->mAdapter.instantiateItem()--->setUserVisibleHint(false)

void populate(){
        
        //根据pageOffsize等参数算出mCurItem,也就是当前显示界面的位置,默认为0,然后将0-mCurItem之间的Fragment全部初始化出来,addItem()方法不断调用(此时还没有去创建Fragment对象,只是内部保存了一个ItemInfo的数组信息)
        //设置要显示的那个item,要显示的Fragment的setUserVisibleHint方法被调用
        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
        //执行创建Fragment的操作,由FragmentPageAdapter实现
        mAdapter.finishUpdate(this);
}

也就是当第一次确定要显示哪一个Fragment的时候,其实Fragment这个对象还并没有创建出来。那么懒加载在Fragment中应该考虑到这一点。

    /**
     * 进行懒加载
     */
    private void lazyFetchDataIfPrepared() {
        // 用户可见fragment && 没有加载过数据 && 视图已经准备完毕
        if (getUserVisibleHint() && !hasFetchData && isViewPrepared) {
            hasFetchData = true;
            lazyFetchData();
        }
    }

你可能感兴趣的:(懒加载---原理分析)