Fragment 的懶加載

Fragment 懶加載是什麼意思?
所謂懶加載,即Fragment 的 UI 對用戶可見時才加載數據。

以前我沒啥經驗,一股腦的從服務器拉取數據之後立馬把數據綁定到 Fragment 的 UI 組件上,導致性能低下。後來知道了這一技術,才明白這纔是移動端加載數據的正確姿勢。

懶加載的技術關鍵點是什麼?
根據定義:所謂懶加載,即Fragment 的 UI 對用戶可見時才加載數據。
需要判斷何時 Fragment 的 UI 纔對用戶可見

如何判斷 Fragment 的 UI 是否對用戶可見?
Fragment 提供了一個方法 public void setUserVisibleHint(boolean isVisibleToUser),API 的註釋如下

Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore

所以,只需要判斷參數 isVisibleToUser 是否爲 True 即可知道該 Fragment 的 UI 是否對用戶可見。

setUserVisibleHint 在什麼時候調用?
對於單個 Fragment,setUserVisibleHint 是不會被調用的,只有該 Fragment 在 ViewPager 裏纔會被調用。所以,我寫了一個 ViewPager + Fragment 的 Demo,打印了一下 Log。

D/Owen_TestFragment: setUserVisibleHint: isVisibleToUser = false
D/Owen_TestFragment: onAttach
D/Owen_TestFragment: onCreate
D/Owen_TestFragment: setUserVisibleHint: isVisibleToUser = true
D/Owen_TestFragment: onCreateView
D/Owen_TestFragment: onActivityCreated
D/Owen_TestFragment: onStart
D/Owen_TestFragment: onResume
D/Owen_TestFragment: onPause
D/Owen_TestFragment: onPause 
D/Owen_TestFragment: onStop
D/Owen_TestFragment: onDestroyView
D/Owen_TestFragment: onDestroy
D/Owen_TestFragment: onDetach

可以看到 setUserVisibleHint 的執行順序是:

setUserVisibleHint(false) -> onAttach -> onCreate -> setUserVisibleHint(true)  -> onCreateView -> onActivityCreated ->.... -> onDetach

代碼
爲了方便,封裝一個基類 LazyLoadFragment,提供一個 loadData() 方法供調用去加載數據

public abstract class LazyLoadFragment extends Fragment {

    /**
     * 控件是否初始化完成
     */
    private boolean isViewCreated;
    /**
     * 數據是否已加載完畢
     */
    private boolean isLoadDataCompleted;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getLayout(), container, false);
        initViews(view);
        isViewCreated = true;
        return view;
    }

    public abstract int getLayout();
    public abstract void initViews(View view);

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && isViewCreated && !isLoadDataCompleted) {
            isLoadDataCompleted = true;
            loadData();
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (getUserVisibleHint()) {
            isLoadDataCompleted = true;
            loadData();
        }
    }

    /**
     * 子類實現加載數據的方法
     */
    public abstract  void loadData();
}

等等,爲什麼 loadData() 會在兩個地方執行?在 setUserVisibleHint 方法裏執行我還能理解,爲什麼 onActivityCreated 也要執行呢?

因爲,ViewPager 默認顯示第一頁,第一頁肯定要先加載數據啊,而且 setUserVisibleHint 的執行順序又是在 onCreatView 之前,同時 onCreatView 需要初始化界面和修改 isViewCreated 的值。所以就需要在 onActivityCreated 裏執行一次咯。

等等
文章寫到這裏,我聽到了一個不同的聲音

ViewPager 不是有 setOffscreenPageLimit(int limit) 方法嗎?我調用 viewPager.setOffscreenPageLimit(0) 不就行了嗎?
我想說:思路是對的,但是這樣做沒效果。爲什麼?看一下 setOffscreenPageLimit 的方法實現就知道了

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

public void setOffscreenPageLimit(int limit) {
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
        mOffscreenPageLimit = limit;
        populate();
    }
}

limit 默認爲 1 ,就算傳一個 0 也無濟於事啊。

總結
懶加載的技術關鍵點
setUserVisibleHint 的執行順序
爲什麼 ViewPager.setOffscreenPageLimit(0) 無效?
參考來源
tablayout+viewpager+fragment組合使用以及懶加載機制 http://yaohepeng.com/2015/10/09/tablayout+viewpager+fragment組合使用以及懶加載機制/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章