之前已經(jīng)寫(xiě)了一篇關(guān)于懶加載的文章,那為什么還要重新寫(xiě)一次呢?因?yàn)槟鞘呛芫弥皩?xiě)的,而且是引用別人的代碼,最近我又用到了懶加載,但是我發(fā)現(xiàn)我看不懂之前寫(xiě)的文章,所以打算重寫(xiě)一次。
項(xiàng)目地址:
https://github.com/994866755/handsomeYe.lazyFragment
一、非懶加載情況下的情況
我們做一個(gè)viewpager然后寫(xiě)3個(gè)fragment,設(shè)置viewpager的緩存為3(.setOffscreenPageLimit(3)),然后在生命周期onCreateView,onResume,onPause,onDestroyView,onDestroy五個(gè)方法中打印。發(fā)現(xiàn)結(jié)果如下:

展示第一個(gè)頁(yè)面的時(shí)候所有頁(yè)面的生命周期都走到onCreate,但是要注意,他們的是都執(zhí)行,但是執(zhí)行的順序不固定,但是執(zhí)行的順序不固定,但是執(zhí)行的順序不固定。重要的事說(shuō)三遍。
有時(shí)候執(zhí)行第一個(gè)頁(yè)面的生命周期前會(huì)先執(zhí)行第二個(gè)頁(yè)面的,目前我也還沒(méi)研究為什么會(huì)發(fā)生這種情況。但先記住,以免出BUG,你要是不放心,可以寫(xiě)一個(gè)攔截器,對(duì)每個(gè)生命周期進(jìn)行監(jiān)聽(tīng)打印。
二、使用懶加載
1. 要使用懶加載,就必須使用setUserVisibleHint這個(gè)方法,我們加入這個(gè)方法再打印,看看結(jié)果:

注意這里,很重要,它先是全設(shè)置false,再把展示的變成true,不過(guò)沒(méi)關(guān)系,因?yàn)閒alse false false true這一系列都發(fā)生在onCreateView之前,所以這里可以把這個(gè)流程當(dāng)成true false false。
2. 設(shè)置懶加載
要設(shè)置懶加載,必須要使用三個(gè)參數(shù),我試過(guò)很多方法,都要使用三個(gè)參數(shù)。如果剛開(kāi)始你直接抄別人的代碼,你肯定記不住三個(gè)參數(shù)是什么意義,自己使用setUserVisibleHint才能深刻的記住怎么去填坑。
(1)假設(shè)我什么都不做,就會(huì)出現(xiàn)上圖中的結(jié)果。
(2) 首先我定義一個(gè)布爾類型,用來(lái)判斷是否第一次展示這個(gè)頁(yè)面,默認(rèn)是true,展示之后把它變成false,命名為isFristShowFragment;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
Log.v("lazy","setUserVisibleHint " + isVisibleToUser);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
str = getArguments().getString("str");
Log.v("lazy","onCreateView "+str);
view = LayoutInflater.from(getActivity()).inflate(R.layout.layout_textview,null);
ButterKnife.inject(this,view);
if (isFristShowFragment && isVisibleToUser) {
setDataToView();
}
return view;
}
private void setDataToView(){
Log.v("lazy","setDataToView "+str);
isFristShowFragment = false;
tvContent.setText(str);
}
我們這樣寫(xiě),在執(zhí)行獲取數(shù)據(jù)的setDataToView()加層判斷,如果是第一次顯示并且當(dāng)前fragment顯示時(shí)才調(diào)用。然后你發(fā)現(xiàn)結(jié)果這樣。

這樣沒(méi)錯(cuò),第一次進(jìn)來(lái)只加載了第一個(gè)頁(yè)面的數(shù)據(jù),但是你滑動(dòng)viewpager切換的時(shí)候發(fā)現(xiàn)第二個(gè)頁(yè)面第三個(gè)頁(yè)面都不執(zhí)行setDataToView(),這是因?yàn)閛nCreate只執(zhí)行了一次,你切換也只執(zhí)行了setUserVisibleHint方法,所以我們需要多加一個(gè)參數(shù)并且在setUserVisibleHint方法內(nèi)做操作。
(3)在setUserVisibleHint內(nèi)做操作
我的思路是加一個(gè)布爾類型isInitView,表示是否執(zhí)行過(guò)初始化的操作,如果執(zhí)行了就變成true。代碼就變成這樣。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
Log.v("lazy","setUserVisibleHint " + isVisibleToUser);
if (isFristShowFragment && isVisibleToUser && isInitView){
setDataToView();
}
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
isInitView = true;
str = getArguments().getString("str");
Log.v("lazy","onCreateView "+str);
view = LayoutInflater.from(getActivity()).inflate(R.layout.layout_textview,null);
ButterKnife.inject(this,view);
if (isFristShowFragment && isVisibleToUser) {
setDataToView();
}
return view;
}
setDataToView()不變。
你會(huì)發(fā)現(xiàn)我在onCreateView加一步操作,isInitView = true。執(zhí)行了初始化的操作后把isInitView 變?yōu)閠rue。
你想想,如果不加這個(gè)值判斷,直接在setUserVisibleHint中這樣寫(xiě):
if (isFristShowFragment && isVisibleToUser ){
setDataToView();
}
那么會(huì)在onCreatView執(zhí)行前就執(zhí)行setDataToView,你想想,在初始化操作之前就執(zhí)行設(shè)置數(shù)據(jù)操作,那必定報(bào)空指針。所以要加個(gè)參數(shù)確保先執(zhí)行初始化之后再執(zhí)行設(shè)置數(shù)據(jù)。
按照上面的做法結(jié)果如下:
剛進(jìn)入顯示第一個(gè)頁(yè)面時(shí):

切換第二個(gè)頁(yè)面:

切換第三個(gè)頁(yè)面:

3. 注意
使用懶加載要注意以下幾點(diǎn):
(1)這種寫(xiě)法你無(wú)法控制生命周期,fragment的生命周期不歸你直接控制,要不然我使用兩個(gè)參數(shù)就能實(shí)現(xiàn)了,你只能控制每個(gè)生命周期內(nèi)的方法,相當(dāng)于在onCreate周期內(nèi)的操作前加一層判斷。
(2)需要三個(gè)參數(shù),不管我怎么試,這一系列操作都需要三個(gè)參數(shù),你可以不按我先這樣的邏輯去寫(xiě),但不管你怎么寫(xiě),怎么設(shè)計(jì)邏輯,我想都需要三個(gè)參數(shù),你只用設(shè)置兩個(gè),另一個(gè)isVisibleToUser是由setUserVisibleHint傳入的。
(3)頁(yè)面少時(shí)使用setOffscreenPageLimit設(shè)置全部緩存,本來(lái)我都已經(jīng)處理了你viewpager的預(yù)加載,只要不擔(dān)心內(nèi)存占用多,你viewpager本身就為我設(shè)置了緩存機(jī)制,我就直接一行代碼調(diào)用多方便。