Fragment實現懶載入,讓應用更優化
一.概述
玩過微信的都知道,微信用的是懶載入的模式,之所以使用懶載入是因為:當使用viewpager+adapter作為應用大的佈局時,viewpager會通過setOffscreenPageLimit來設定預載入的專案,不設定setOffscreenPageLimit,則預設為1(設定0無效,可以檢視該方法原始碼知道),也就是當我們開啟應用看到的時候fragmentOne時,實際上其他fragment(例如fragmentSecond)也進行了載入,只不過沒有顯示出來罷了,但是這樣就造成了不必要的資源浪費(例如,fragmentSecond沒有顯示,但是卻進行了大量的網路載入操作)。
基於上述情況,就有了懶載入方式的誕生(即只加載當前顯示頁面且只加載一次,滑動到其他頁面時才載入其他頁面資料,當再滑動到已載入過資料的頁面時不再進行資料載入操作,若想要重新整理資料,再呼叫相應的載入資料方法就好了)
二.Fragment生命週期基本探索
為了更好的知道懶載入的實現原理,下面通過幾個測試來學習下。
測試的幾個檔案如下
(可以看到有3個fragment,由FragmentPagerAdapter+viewpager構成)
- 情況一
setOffscreenPageLimit(1) (即預設情況)
2.情況二
setOffscreenPageLimit(3) (即設定預載入數目為實際fragment數目)
三.Fragment懶載入實現
可以看到無論是情況一還是情況二,fragment都會呼叫fragment的setUserVisibleHint進行判斷,所以我們就需要在這裡做文章。
1.當isVisibleToUser 為true則進行資料載入,當isVisibleToUser為false則不進行資料載入
2.對於已經載入過資料的fragment,再次被滑動到也不在進行載入資料,也就是每個fragment僅做一次資料載入工作
下面就來看程式碼實現
主要程式碼都在BaseFragment中,整個專案大家可以在最後給出的地址處下載
public abstract class BaseFragment extends Fragment {
private boolean isVisible = false;//當前Fragment是否可見
private boolean isInitView = false;//是否與View建立起對映關係
private boolean isFirstLoad = true;//是否是第一次載入資料
private View convertView;
private SparseArray<View> mViews;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LogUtil.m(" " + this.getClass().getSimpleName());
convertView = inflater.inflate(getLayoutId(), container, false);
mViews = new SparseArray<>();
initView();
isInitView = true;
lazyLoadData();
return convertView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
LogUtil.m(" " + this.getClass().getSimpleName());
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
LogUtil.m("context" + " " + this.getClass().getSimpleName());
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
LogUtil.m("isVisibleToUser " + isVisibleToUser + " " + this.getClass().getSimpleName());
if (isVisibleToUser) {
isVisible = true;
lazyLoadData();
} else {
isVisible = false;
}
super.setUserVisibleHint(isVisibleToUser);
}
private void lazyLoadData() {
if (isFirstLoad) {
LogUtil.m("第一次載入 " + " isInitView " + isInitView + " isVisible " + isVisible + " " + this.getClass().getSimpleName());
} else {
LogUtil.m("不是第一次載入" + " isInitView " + isInitView + " isVisible " + isVisible + " " + this.getClass().getSimpleName());
}
if (!isFirstLoad || !isVisible || !isInitView) {
LogUtil.m("不載入" + " " + this.getClass().getSimpleName());
return;
}
LogUtil.m("完成資料第一次載入");
initData();
isFirstLoad = false;
}
/**
* 載入頁面佈局檔案
* @return
*/
protected abstract int getLayoutId();
/**
* 讓佈局中的view與fragment中的變數建立起對映
*/
protected abstract void initView();
/**
* 載入要顯示的資料
*/
protected abstract void initData();
/**
* fragment中可以通過這個方法直接找到需要的view,而不需要進行型別強轉
* @param viewId
* @param <E>
* @return
*/
protected <E extends View> E findView(int viewId) {
if (convertView != null) {
E view = (E) mViews.get(viewId);
if (view == null) {
view = (E) convertView.findViewById(viewId);
mViews.put(viewId, view);
}
return view;
}
return null;
}
}
可以看到initView方法是在onCreateView中呼叫,而initData只有執行過onCreateView才會呼叫,這樣的順序安排就不會導致在initData中執行資料載入過程,找不到需要的view而報錯。
專案結構
演示效果
可以看到fragment只會進行執行一次initData,懶載入到此完整,下一篇將在此基礎上進行高仿微信的效果