1. 程式人生 > >Fragment實現懶載入,讓應用更優化

Fragment實現懶載入,讓應用更優化

一.概述

玩過微信的都知道,微信用的是懶載入的模式,之所以使用懶載入是因為:當使用viewpager+adapter作為應用大的佈局時,viewpager會通過setOffscreenPageLimit來設定預載入的專案,不設定setOffscreenPageLimit,則預設為1(設定0無效,可以檢視該方法原始碼知道),也就是當我們開啟應用看到的時候fragmentOne時,實際上其他fragment(例如fragmentSecond)也進行了載入,只不過沒有顯示出來罷了,但是這樣就造成了不必要的資源浪費(例如,fragmentSecond沒有顯示,但是卻進行了大量的網路載入操作)。
這裡寫圖片描述
基於上述情況,就有了懶載入方式的誕生(即只加載當前顯示頁面且只加載一次,滑動到其他頁面時才載入其他頁面資料,當再滑動到已載入過資料的頁面時不再進行資料載入操作,若想要重新整理資料,再呼叫相應的載入資料方法就好了)

二.Fragment生命週期基本探索

為了更好的知道懶載入的實現原理,下面通過幾個測試來學習下。
測試的幾個檔案如下
(可以看到有3個fragment,由FragmentPagerAdapter+viewpager構成)

這裡寫圖片描述

  1. 情況一
    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,懶載入到此完整,下一篇將在此基礎上進行高仿微信的效果