安卓學(xué)習(xí) - Fragment的懶加載

轉(zhuǎn)載請附上原博客連接 http://www.reibang.com/p/837cdd4cf371

懶加載的應(yīng)用場景和作用:

在我們開發(fā)APP的過程中痪欲,經(jīng)常會用到ViewPager+Fragment的UI結(jié)構(gòu)(微信主界面就是這樣組成的)每界。通過左右滑動七兜,ViewPager加載不同的Fragment進(jìn)行顯示泥从。ViewPager在加載當(dāng)前應(yīng)當(dāng)顯示的Fragment的時候,會同時將該Fragment前后相鄰的Fragment也加載到內(nèi)存中缸濒。這樣妇拯,當(dāng)幾個Fragment中都有很多耗費資源的初始化操作時,可能會因網(wǎng)絡(luò)阻塞造成當(dāng)前頁面初始化緩慢的問題脓钾。因此我們需要只在Fragment用戶可見的時候才加載數(shù)據(jù)售睹,這就是懶加載。

首先我們來證實一下在使用Fragment+ViewPager的時候可训。ViewPager會提前加載當(dāng)前Fragment左右相鄰的Fragment昌妹。

我們像往常一樣,在主界面中使用ViewPager握截,如下面代碼所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tinymonster.lazyloadtest.MainActivity">

    <android.support.v4.view.ViewPager
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/viewPager"
        />
</LinearLayout>

自定義Fragment捺宗,在生命周期內(nèi)打印信息。

public class MyFragment extends Fragment{
    private String data;

    public MyFragment(){
    }

    public MyFragment(String data){
        this.data=data;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.e(data,"onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(data,"onCreate");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.e(data,"onCreateView");
        View view=inflater.inflate(R.layout.my_fragment,null);
        TextView textView=(TextView)view.findViewById(R.id.text);
        textView.setText(data);
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.e(data,"onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.e(data,"onResume");
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.e(data,"setUserVisibleHint:"+isVisibleToUser);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.e(data,"onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(data,"onDestroy");
    }

}

在主界面代碼中給ViewPager添加Fragment川蒙,并且設(shè)置首先顯示第三個Fragment蚜厉。

public class MainActivity extends AppCompatActivity {
    private ViewPager viewPager;
    private MyAdapter myAdapter;
    List<Fragment> list=new ArrayList<>();
    @Override

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=(ViewPager)findViewById(R.id.viewPager);
        list.add(new MyFragment("Fragment_1"));
        list.add(new MyFragment("Fragment_2"));
        list.add(new MyFragment("Fragment_3"));
        list.add(new MyFragment("Fragment_4"));
        list.add(new MyFragment("Fragment_5"));
        myAdapter=new MyAdapter(getSupportFragmentManager(),list);
        viewPager.setAdapter(myAdapter);
        viewPager.setCurrentItem(2);
    }
}

運(yùn)行程序,打印信息如下畜眨。


打印生命周期信息

我們從打印信息中可以看出昼牛,ViewPager在加載Fragment_3的同時,也調(diào)用的Fragment_2和Fragment_4的生命周期(onAttach,onCreate,onCreateView,onStart,onResume)康聂。因此贰健,如果Fragment_2,F(xiàn)ragment_3和Fragment_4的初始化中有很多耗費網(wǎng)絡(luò)資源的操作時恬汁,可能造成網(wǎng)絡(luò)阻塞伶椿,使得需要顯示的Fragment_3遲遲不能初始化。

ViewPager源碼分析

為什么會在加載當(dāng)前Fragment的時候也把他左右兩個Fragment也加載到內(nèi)存中呢?下面我們看一下ViewPager的源碼脊另。
首先我們看ViewPager中的onMeasure()导狡。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 沒有指定的情況下寬高為0 ;
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
                getDefaultSize(0, heightMeasureSpec));

        final int measuredWidth = getMeasuredWidth();
        final int maxGutterSize = measuredWidth / 10;
        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);

        // 得到可用空間
        int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();
        int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();

        //計算每一個孩子所用空間
        int size = getChildCount();
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp != null && lp.isDecor) {
                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                    int widthMode = MeasureSpec.AT_MOST;
                    int heightMode = MeasureSpec.AT_MOST;
                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;

                    if (consumeVertical) {
                        widthMode = MeasureSpec.EXACTLY;
                    } else if (consumeHorizontal) {
                        heightMode = MeasureSpec.EXACTLY;
                    }

                    int widthSize = childWidthSize;
                    int heightSize = childHeightSize;
                    if (lp.width != LayoutParams.WRAP_CONTENT) {
                        widthMode = MeasureSpec.EXACTLY;
                        if (lp.width != LayoutParams.MATCH_PARENT) {
                            widthSize = lp.width;
                        }
                    }
                    if (lp.height != LayoutParams.WRAP_CONTENT) {
                        heightMode = MeasureSpec.EXACTLY;
                        if (lp.height != LayoutParams.MATCH_PARENT) {
                            heightSize = lp.height;
                        }
                    }
                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
                    child.measure(widthSpec, heightSpec);

                    if (consumeVertical) {
                        childHeightSize -= child.getMeasuredHeight();
                    } else if (consumeHorizontal) {
                        childWidthSize -= child.getMeasuredWidth();
                    }
                }
            }
        }
// 合成測量規(guī)格
        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);

        // Make sure we have created all fragments that we need to have shown.
        mInLayout = true;
        //填充
        populate();
        mInLayout = false;

        // 測量調(diào)用每一個孩子的measure()方法
        size = getChildCount();
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                if (DEBUG) {
                    Log.v(TAG, "Measuring #" + i + " " + child + ": " + mChildWidthMeasureSpec);
                }

                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp == null || !lp.isDecor) {
                    final int widthSpec = MeasureSpec.makeMeasureSpec(
                            (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);
                    child.measure(widthSpec, mChildHeightMeasureSpec);
                }
            }
        }
    }

ViewPager的onMeasure()方法進(jìn)行了如下操作:1.確定了自身可用空間,2.遍歷每一個裝飾view偎痛,測量他們的大小和計算剩余空間旱捧,3.調(diào)用populate()方法根據(jù)頁面緩存顯示進(jìn)行頁面銷毀與重建,4.測量每一個孩子踩麦。

下面我們再看一下populate()方法枚赡。

void populate() {
        // 將當(dāng)前選中position放入
        populate(mCurItem);
    }
    void populate(int newCurrentItem) {
        // 舊的選中的 ItemInfo
        ItemInfo oldCurInfo = null;
        if (mCurItem != newCurrentItem) {
            //infoForPosition方法得到 舊 ItemInfo
            oldCurInfo = infoForPosition(mCurItem);
            mCurItem = newCurrentItem;
        }

        if (mAdapter == null) {
            //排序繪制view的順序
            sortChildDrawingOrder();
            return;
        }

        if (mPopulatePending) {
            if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
            sortChildDrawingOrder();
            return;
        }

        // 不在window中時
        if (getWindowToken() == null) {
            return;
        }
// 調(diào)用 startUpdate 方法
        mAdapter.startUpdate(this);
      //頁的限制
        final int pageLimit = mOffscreenPageLimit;
        //開始頁
        final int startPos = Math.max(0, mCurItem - pageLimit);
        //總數(shù)
        final int N = mAdapter.getCount();
        // 結(jié)束頁
        final int endPos = Math.min(N - 1, mCurItem + pageLimit);
         // 如果預(yù)期數(shù)量不一致
        if (N != mExpectedAdapterCount) {
            String resName;
            try {
                //得到資源名稱
                resName = getResources().getResourceName(getId());
            } catch (Resources.NotFoundException e) {
                resName = Integer.toHexString(getId());
            }
            throw new IllegalStateException("The application's PagerAdapter changed the adapter's"
                    + " contents without calling PagerAdapter#notifyDataSetChanged!"
                    + " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N
                    + " Pager id: " + resName
                    + " Pager class: " + getClass()
                    + " Problematic adapter: " + mAdapter.getClass());
        }

        // Locate the currently focused item or add it if needed.
        int curIndex = -1;
        ItemInfo curItem = null;
        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
             //得到當(dāng)前 ItemInfo
            final ItemInfo ii = mItems.get(curIndex);
            //如果 position 大于 mCurItem
            if (ii.position >= mCurItem) {
                // cutlItem=ii
                if (ii.position == mCurItem) curItem = ii;
                break;
            }
        }
// 如果等于空  添加
        if (curItem == null && N > 0) {
            curItem = addNewItem(mCurItem, curIndex);
        }
// 如果 curItem 不為空
        if (curItem != null) {
            float extraWidthLeft = 0.f;
            //-1
            int itemIndex = curIndex - 1;
            //ii  如果itemIndex 大于 0 取出,否則null
            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
            //得到可用寬度
            final int clientWidth = getClientWidth();
           // 計算左邊需要的寬度
            final float leftWidthNeeded = clientWidth <= 0 ? 0 :
                    2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
            // 從'選中的下標(biāo)-1' --> 0  遍歷
            for (int pos = mCurItem - 1; pos >= 0; pos--) {
                //如果額外的 大于 需要的   和  pos 小于 startPos
                if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                    if (ii == null) {
                        break;
                    }
                    // 移除
                    if (pos == ii.position && !ii.scrolling) {
                        mItems.remove(itemIndex);
                        mAdapter.destroyItem(this, pos, ii.object);
                        if (DEBUG) {
                            Log.i(TAG, "populate() - destroyItem() with pos: " + pos
                                    + " view: " + ((View) ii.object));
                        }
                        itemIndex--;
                        curIndex--;
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    }
                } else if (ii != null && pos == ii.position) {    // 如果選中的上一個有 infoItem 
                    // extraWidthLeft 加上  ii.widthFactor
                    extraWidthLeft += ii.widthFactor;
                    itemIndex--;
                    //拿上一個 infoItem
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                } else {
                    // 新增一個 INfoItem
                    ii = addNewItem(pos, itemIndex + 1);
                    // 將占用的寬度比例加進(jìn)來
                    extraWidthLeft += ii.widthFactor;
                    curIndex++;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                }
            }
            // 右邊消耗的比例
            float extraWidthRight = curItem.widthFactor;
            //右邊index
            itemIndex = curIndex + 1;
            if (extraWidthRight < 2.f) {
                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                // 計算右邊需要的寬度
                final float rightWidthNeeded = clientWidth <= 0 ? 0 :
                        (float) getPaddingRight() / (float) clientWidth + 2.f;
                for (int pos = mCurItem + 1; pos < N; pos++) {
                    // 如果 pos大于 endPos
                    if (extraWidthRight >= rightWidthNeeded && pos > endPos) {
                        if (ii == null) {
                            break;
                        }
                        //移除
                        if (pos == ii.position && !ii.scrolling) {
                            mItems.remove(itemIndex);
                            mAdapter.destroyItem(this, pos, ii.object);
                            if (DEBUG) {
                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos
                                        + " view: " + ((View) ii.object));
                            }
                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                        }
                    } else if (ii != null && pos == ii.position) {
                        // 相等 那么取出數(shù)據(jù)
                        extraWidthRight += ii.widthFactor;
                        itemIndex++;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    } else {
                        // 否則 新建 infoItem
                        ii = addNewItem(pos, itemIndex);
                        itemIndex++;
                        extraWidthRight += ii.widthFactor;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    }
                }
            }
            // 計算偏移量
            calculatePageOffsets(curItem, curIndex, oldCurInfo);
        }

        if (DEBUG) {
            Log.i(TAG, "Current page list:");
            for (int i = 0; i < mItems.size(); i++) {
                Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
            }
        }
        // 通知適配器 哪個個是主頁
        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
       // 結(jié)束更新
        mAdapter.finishUpdate(this);

        // Check width measurement of current pages and drawing sort order.
        // Update LayoutParams as needed.
        final int childCount = getChildCount();
        // 遍歷view
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            lp.childIndex = i;
            if (!lp.isDecor && lp.widthFactor == 0.f) {
                // 0 means requery the adapter for this, it doesn't have a valid width.
                final ItemInfo ii = infoForChild(child);
                if (ii != null) {
                    // 設(shè)置屬性
                    lp.widthFactor = ii.widthFactor;
                    lp.position = ii.position;
                }
            }
        }
        //排序
        sortChildDrawingOrder();

        if (hasFocus()) {
            View currentFocused = findFocus();
            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
            if (ii == null || ii.position != mCurItem) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    ii = infoForChild(child);
                   //如果拿到當(dāng)前view
                    if (ii != null && ii.position == mCurItem) {
                         //設(shè)置聚焦
                        if (child.requestFocus(View.FOCUS_FORWARD)) {
                            break;
                        }
                    }
                }
            }
        }
    }

上面一段代碼很長谓谦,主要內(nèi)容就是首先根據(jù)mOffscreenPageLimit(頁面限制贫橙,用于確定緩存Fragment的數(shù)量)和mCurItem(當(dāng)前應(yīng)顯示頁碼)計算出了應(yīng)加載的Fragment的頁碼數(shù),

//頁的限制
        final int pageLimit = mOffscreenPageLimit;
        //開始頁
        final int startPos = Math.max(0, mCurItem - pageLimit);
        //總數(shù)
        final int N = mAdapter.getCount();
        // 結(jié)束頁
        final int endPos = Math.min(N - 1, mCurItem + pageLimit);

然后根據(jù)計算得到的開始頁和結(jié)束頁調(diào)用Adapter移除或者加載Fragment反粥。
原來ViewPager中有一個變量(mOffscreenPageLimit)卢肃,用來確定加載的Fragment數(shù)量,那么我們修改這個變量不久行了嗎星压?
繼續(xù)往下看践剂,ViewPager中有提供設(shè)置mOffscreenPageLimit的函數(shù)鬼譬。

//默認(rèn)的緩存頁面數(shù)量(常量)
private static final int DEFAULT_OFFSCREEN_PAGES = 1;

//緩存頁面數(shù)量(變量)
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;

public void setOffscreenPageLimit(int limit) {
    //當(dāng)我們手動設(shè)置的limit數(shù)小于默認(rèn)值1時,limit值會自動被賦值為默認(rèn)值1(即DEFAULT_OFFSCREEN_PAGES)
    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) {
        //經(jīng)過前面的攔截判斷后,將limit的值設(shè)置給mOffscreenPageLimit,用于
        mOffscreenPageLimit = limit;
        populate();
    }
}

根據(jù)上面代碼我們可知娜膘,如果我們設(shè)置的mOffscreenPageLimit小于DEFAULT_OFFSCREEN_PAGE,就強(qiáng)制給mOffscreenPageLimit賦值為DEFAULT_OFFSCREEN_PAGE优质。這個DEFAULT_OFFSCREEN_PAGE的值就是1竣贪。
現(xiàn)在終于真相大白了,ViewPager通過一定的邏輯判斷來確保至少會預(yù)加載左右兩側(cè)相鄰的1個頁面巩螃,我們不能通過簡單設(shè)置mOffscreenPageLimit來達(dá)到懶加載的目的演怎。

懶加載實現(xiàn)原理

還記得我們前面再Fragment中打印的生命周期嗎?我還在這個函數(shù)setUserVisibleHint(boolean isVisibleToUser)中打印了信息避乏。根據(jù)函數(shù)名我們可以猜出來這個函數(shù)與用戶可見有關(guān)爷耀。查看這個函數(shù)的源碼

    /**
     * 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.
     *
     * <p>An app may set this to false to indicate that the fragment's UI is
     * scrolled out of visibility or is otherwise not directly visible to the user.
     * This may be used by the system to prioritize operations such as fragment lifecycle updates
     * or loader ordering behavior.</p>
     *
     * <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle.
     * and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p>
     *
     * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
     *                        false if it is not.
     */
    public void setUserVisibleHint(boolean isVisibleToUser) {
        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
                && mFragmentManager != null && isAdded()) {
            mFragmentManager.performPendingDeferredStart(this);
        }
        mUserVisibleHint = isVisibleToUser;
        mDeferStart = mState < STARTED && !isVisibleToUser;
    }

翻譯上面一段注釋:設(shè)置一個提示用來表示這個Fragment是否被用戶可見。應(yīng)用程序可以將此設(shè)置為false拍皮,以指示Fragment的UI被滾動到不可見的位置歹叮,或者用戶無法直接看到。系統(tǒng)可以使用它來對諸如片段生命周期更新或加載器排序行為等操作進(jìn)行優(yōu)先級排序铆帽。這個函數(shù)可以在Fragment的生命周期以外調(diào)用咆耿。

總之,當(dāng)Fragment從可見變?yōu)椴豢梢娛腔蛘邚牟豢梢娮優(yōu)榭梢姇r都會自動調(diào)用這個函數(shù)爹橱,可見是輸入值為TRUE萨螺,不可見時輸入值為FALSE。因此我們可以根據(jù)這個函數(shù)的輸入值來判斷Fragment是否可見,從而進(jìn)行懶加載慰技。

因為Fragment在由可見變?yōu)椴豢梢娡终担陀刹豢梢娮優(yōu)榭梢姷臅r候都會調(diào)用setUserVisibleHint()函數(shù),因此我們可以在這個函數(shù)實現(xiàn)懶加載的邏輯惹盼。在這個函數(shù)中庸汗,我們判斷只有當(dāng)Fragment可見 & Fragment的View已經(jīng)加載完成 & Fragment沒有加載過數(shù)據(jù) 的條件下才進(jìn)行數(shù)據(jù)加載。

根據(jù)這樣的思路手报,我們設(shè)置了幾個標(biāo)記位:

    private boolean isViewInit; //view是否已經(jīng)加載完成
    private boolean isVisible; //是否可見
    private boolean isFirstLoad = true;//是否第一次加載數(shù)據(jù)蚯舱,默認(rèn)為TURE,既默認(rèn)為第一次加載數(shù)據(jù)

設(shè)置了一個函數(shù)來模擬從網(wǎng)絡(luò)加載數(shù)據(jù)

    /**
     * 模擬加載數(shù)據(jù)
     */
    protected void loadData(){
        Log.e(data,"加載數(shù)據(jù)掩蛤!");
    }

然后將懶加載的邏輯寫成下面一個函數(shù)

    /**
     * 懶加載邏輯
     */
    private void lazyLoad() {
        if (!isViewInit || !isVisible || !isFirstLoad) {   //view加載完成&可見&第一次加載時才加載數(shù)據(jù)
            return;
        }
        loadData();   //加載數(shù)據(jù)
        isFirstLoad = false;   //更新標(biāo)志位
    }

注意枉昏,別忘記了在onActivityCreated()中調(diào)用懶加載函數(shù),因為第一個要顯示的Fragment在由不可見變?yōu)榭梢娬{(diào)用setUserVisibleHint()函數(shù)時揍鸟,該Fragment的View還沒有初始化完成兄裂,導(dǎo)致第一個顯示的Fragment無法第一次出現(xiàn)時無法加載數(shù)據(jù),必須重新滑動一下才能加載數(shù)據(jù)阳藻。

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        isViewInit=true;    //設(shè)置標(biāo)志位晰奖,view表示初始化完成
        lazyLoad();    //懶加載
    }

setUserVisibleHint()函數(shù)中調(diào)用懶加載。

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.e(data,"setUserVisibleHint:"+isVisibleToUser);
        if(isVisibleToUser){
            isVisible=true;
            lazyLoad();
        }else {
            isVisible=false;
        }
    }

以上就是我對懶加載的理解腥泥。如果你覺得我寫的有什么不對或者有什么補(bǔ)充的請留言匾南。
代碼已上傳至GitHub https://github.com/Tiny-Monster/LazyLoadTest
參考博客
https://blog.csdn.net/chengkun_123/article/details/73694936
http://www.reibang.com/p/b8fe093a9d4b

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蛔外,隨后出現(xiàn)的幾起案子蛆楞,更是在濱河造成了極大的恐慌,老刑警劉巖夹厌,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豹爹,死亡現(xiàn)場離奇詭異,居然都是意外死亡矛纹,警方通過查閱死者的電腦和手機(jī)臂聋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門或南,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迎献,你說我怎么就攤上這事∮趸校” “怎么了扒秸?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伴奥。 經(jīng)常有香客問我,道長拾徙,這世上最難降的妖魔是什么洲炊? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮尼啡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崖瞭。我一直安慰自己,他們只是感情好书聚,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布雌续。 她就那樣靜靜地躺著,像睡著了一般驯杜。 火紅的嫁衣襯著肌膚如雪受啥。 梳的紋絲不亂的頭發(fā)上艇肴,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天再悼,我揣著相機(jī)與錄音,去河邊找鬼冲九。 笑死跟束,一個胖子當(dāng)著我的面吹牛莺奸,可吹牛的內(nèi)容都是我干的冀宴。 我是一名探鬼主播略贮,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼览妖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起讽膏,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤府树,失蹤者是張志新(化名)和其女友劉穎俐末,沒想到半個月后奄侠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡遭铺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了甫题。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涂召。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖果正,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秋泳,我是刑警寧澤潦闲,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布迫皱,位于F島的核電站,受9級特大地震影響卓起,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昼弟,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一奕筐、第九天 我趴在偏房一處隱蔽的房頂上張望蚕键。 院中可真熱鬧衰粹,春花似錦、人聲如沸铝耻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泡态。三九已至,卻和暖如春桐汤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怔毛。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工腾降, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留拣度,地道東北人螃壤。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像冤馏,于是被迫代替她去往敵國和親蚁滋。 傳聞我的和親對象是個殘疾皇子赘淮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容