懶加載問題的完美解決
前言:
ViewPager會對其中的Fragment進行預加載。也就是說使用者第一次打開第一個界面的時候,不僅第一個界面會進行加載,其他的界面也會進行界面的預加載。這樣就會帶來界面啟動加載慢,浪費系統資源和使用者流量的不好的體驗。而Fragment的懶加載恰好可以解決這個問題.
定義:
什麼是懶加載?就是被動加載,當Fragment頁面可見時,才從網絡加載資料并顯示出來
懶加載條件:
1.View視圖加載完畢,即onCreateView()執行完成
2.目前Fragment可見,即setUserVisibleHint()的參數為true
3.初次加載,即防止多次滑動重複加載
是以要設定三個布爾類型參數,進行判斷
完美解決方案:
建立一個抽象父類BaseLazyLoadFragment,在父類中實作懶加載過程,把接口暴露給子類去實作
其中的問題:
問題一:setUserVisibleHint方法在切換界面時會多次調用,而我們隻希望他被調用一次,既第一次進入頁面時被調用。
解決:設定一個布爾類型參數mIsFirstLoad,保證是第一次加載
問題二:setUserVisibleHint是Fragment的可見性變化時回調的方法,而onCreateView是Fragment建立視圖時回調的方法。但是我們無法保證setUserVisibleHint的調用發生在onCreateView(也就是視圖建立)之後。那麼我們就是在視圖還沒有建立時進行資料加載,而往往資料的加載會對視圖控件進行操作,那麼就會造成空指針的異常發生(因為視圖控件還沒有初始化)。
解決:在設定一個布爾類型參數mIsPrepare,這樣就能保證視圖建立完成之前不會進行資料加載的操作。
問題三:setUserVisibleHint這個方法隻在Fragment的可見性改變的時候才會被調用,如果第一次進入頁面之後setUserVisibleHint先被調用,這時視圖還沒有完成建立,是以資料加載操作不會被調用。而之後沒有切換頁面,Fragment的可見性也就不會發生改變了,setUserVisibleHint也就不會被調用了,那麼資料加載也就不會被執行了。
解決:既然要保證在視圖建立完成後要進行一次資料加載,那麼就在onCreateView方法中手動調用一次資料加載就好了
當然在onCreateView中確定了View已經準備好時,将mPrepare置為true,在setUserVisibleHint中確定了目前可見時,mIsVisible置為true,第一次加載完畢後則将mIsFirstLoad置為false,避免重複加載。
代碼如下(可根據需求定義抽象方法)
public abstract class BaseLazyLoadFragment extends Fragment {
boolean mIsPrepare = false; //視圖還沒準備好
boolean mIsVisible= false; //不可見
boolean mIsFirstLoad = true; //第一次加載
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = initView(inflater, container);
mIsPrepare=true;
startLoad();
return view;
}
private void startLoad() {
if (!mIsPrepare || !mIsVisible||!mIsFirstLoad) {
return;
}
onLazyLoad();
//資料加載完畢,恢複标記,防止重複加載
mIsFirstLoad = false;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
//isVisibleToUser這個boolean值表示:該Fragment的UI 使用者是否可見
if (isVisibleToUser) {
mIsVisible = true;
startLoad();
} else {
mIsVisible = false;
}
super.setUserVisibleHint(isVisibleToUser);
}
@Override
public void onDestroyView() {
mIsFirstLoad=true;
mIsPrepare=false;
mIsVisible = false;
super.onDestroyView();
}
//資料加載接口,留給子類實作
public abstract void onLazyLoad();
//初始化視圖接口,子類必須實作
public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container);
}
view繪制導緻的卡頓優化
fragment的加載最為耗時的步驟主要有兩個,
一個是Fragment建立(尤其是建立View的過程),
另一個就是讀取資料填充到View上的過程。懶加載能夠解決後者所造成的卡頓,但是針對前者來說,并沒有效果
1.設定緩存頁面數
viewPager.setOffscreenPageLimit(int limit) 能夠有效地一次性緩存多個Fragment,這樣就能夠解決在之後每次切換時不會建立執行個體對象,看起來也會流暢。但是這樣的做法,最大的缺點就是容易造成第一次啟動時非常緩慢!如果第一次啟動時間滿足要求的話,就使用這種簡單地辦法吧。
2.避免Fragment的銷毀
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// super.destroyItem(container, position, object);
}
把中間的代碼注釋掉就行了,這樣就可以避免Fragment的銷毀過程
源碼的這個過程之中包含了對FragmentInstanceState的儲存!這也是FragmentStatePagerAdapter的精髓之處,如果注釋掉,一旦Activity被回收進入異常銷毀狀态,Fragment就無法恢複之前的狀态,是以這種方法也是有纰漏和局限性的
3.避免重複建立View