懶加載的本意就是敷搪,讓界面顯示的時候再去加載數(shù)據(jù)。
對于Fragment來說留量,他的onCreateView()方法被執(zhí)行了窄赋,界面才會出來。
ViewPager+Fragment模式
我們一般的做法是這樣的
onCreate(){
mViewPager.setAdapter((new FragmentStatePagerAdapter(...));
mViewPager.setCurrentItem(1);
}
在這里楼熄,要稍微的解釋下忆绰,這兩個方法究竟在ViewPager中做了什么。
剛學Android經(jīng)常犯一個錯誤孝赫,就是在onCreate()方法中去拿某個View的寬高较木,比如,在這里mViewPager.getWidth()肯定為0青柄,那么為什么為0呢?一般的解釋是此時整個View樹還沒有完成測量预侯、布局的操作致开。那么View樹的第一次測量布局究竟是什么時候執(zhí)行的呢?
為了解釋我這個困惑萎馅,嘗試著在onResume()中去拿到ViewPager的寬高双戳,不出所料,肯定還是0糜芳。
View樹的第一次測量布局
在ActivityThread的handleResumeActivity()中飒货,在之前的onCreate()中魄衅,整個View樹的數(shù)據(jù)已經(jīng)創(chuàng)建出來,但是還沒有顯示出來塘辅,所以晃虫,在這里,執(zhí)行一個IPC操作扣墩,將View樹添加到WMS中哲银,那么在客戶端進程中的流程是ViewRootImpl的setView()--->requestLayout(),該方法發(fā)送一個scheduleTraversals()的異步任務呻惕,注意荆责,也就是是說,當Activity的onResume()方法執(zhí)行時亚脆,只是發(fā)送了一個異步的scheduleTraversals任務到UI隊列中去做院,要等到下一次UI線程處理隊列中的這個任務時,才會執(zhí)行濒持。所以键耕,在onResume()中也無法拿到寬高。
setAdapter()方法
經(jīng)過上面的分析弥喉,也就是說setAdapter()執(zhí)行的時候郁竟,界面還沒有出來。
public void setAdapter(...){
//......
if(!wasFirstLayout) { //是否是第一次布局由境,默認為true棚亩,當onLayout執(zhí)行后賦值為false,所以這里會執(zhí)行requestLayout()方法虏杰,發(fā)送一個異步布局消息讥蟆。
populate();
} else {
requestLayout();
}
}
假設后面我們執(zhí)行了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);
}
}
這里會執(zhí)行if中的代碼纺阔,也就是該方法確定了mCurItem,也就是界面顯示的時候究竟顯示哪一個瘸彤,此時是1。然后笛钝,也是發(fā)送了一個異步布局消息质况。
測量布局
onMeasure()方法
// Make sure we have created all fragments that we need to have shown.
mInLayout = true;
populate(); //根據(jù)適配器提供的數(shù)據(jù),創(chuàng)建出相關數(shù)據(jù)玻靡,便于后面的繪制
mInLayout = false;
// Page views next.
size = getChildCount(); //當populate方法執(zhí)行完后结榄,子孩子已經(jīng)被添加到ViewPager中去
setUserVisibleHint()什么時候執(zhí)行
populate()--->addItem--->mAdapter.instantiateItem()--->setUserVisibleHint(false)
void populate(){
//根據(jù)pageOffsize等參數(shù)算出mCurItem,也就是當前顯示界面的位置囤捻,默認為0臼朗,然后將0-mCurItem之間的Fragment全部初始化出來,addItem()方法不斷調(diào)用(此時還沒有去創(chuàng)建Fragment對象,只是內(nèi)部保存了一個ItemInfo的數(shù)組信息)
//設置要顯示的那個item,要顯示的Fragment的setUserVisibleHint方法被調(diào)用
mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
//執(zhí)行創(chuàng)建Fragment的操作视哑,由FragmentPageAdapter實現(xiàn)
mAdapter.finishUpdate(this);
}
也就是當?shù)谝淮未_定要顯示哪一個Fragment的時候绣否,其實Fragment這個對象還并沒有創(chuàng)建出來。那么懶加載在Fragment中應該考慮到這一點挡毅。
/**
* 進行懶加載
*/
private void lazyFetchDataIfPrepared() {
// 用戶可見fragment && 沒有加載過數(shù)據(jù) && 視圖已經(jīng)準備完畢
if (getUserVisibleHint() && !hasFetchData && isViewPrepared) {
hasFetchData = true;
lazyFetchData();
}
}