1. 程式人生 > >探究ListView 的快取機制

探究ListView 的快取機制

概述

ListView 是繼承AbListView,AbListView是所有列表類控制元件的基類。

ListView的資料載入

在ListView資料載入中最關鍵的一個函式就是makeAndAddView(),這個函式的作用就獲得一個ChildView並把該ChildView新增到List中,具體見原始碼分析:

private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
        boolean selected) {
    View child;//即ChildView

	//如果資料沒有發生改變
    if (!mDataChanged) {
        //優先從迴圈器中獲取該位置的檢視
        // Try to use an existing view for this position
        child = mRecycler.getActiveView(position);
        if (child != null) {
            // Found it -- we're using an existing child
            //如果找到了就直接新增到List中
            // This just needs to be positioned
            setupChild(child, position, y, flow, childrenLeft, selected, true);

            return child;
        }
    }

	//如果資料發生了改變,則在該位置上新建一個檢視,或者如果可能的話轉換一個已經沒有用的檢視(可能是當整個ListView其他位置發生了變化,但是該位置的ChildView並未發生任何變化)
    // Make a new view for this position, or convert an unused view if possible
    child = obtainView(position, mIsScrap);

    // This needs to be positioned and measured
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);

	//返回該childView
    return child;
}

ListView的快取機制

當ListView發生滑動操作時,若干已經載入的ChildView會被因滑動而被暫時隱藏掉,為了避免下次顯示再重新載入,這時ListView的快取機制就會被觸發,即執行layoutChildren()函式(其實任何觸碰事件都會觸發,即onTouchEvent() -。-)。

那麼ListView的快取機制是依靠什麼來快取的呢?答案就是AbListView中 的內部類RecycleBin。關於RecycleBin的具體作用,原始碼中的註釋已經解釋的非常清楚了,在此就不在贅述。

 /**
 * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
 * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
 * start of a layout. By construction, they are displaying current information. At the end of
 * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
 * could potentially be used by the adapter to avoid allocating views unnecessarily.
 *... ...
 */

當需要快取ActiveViews時會呼叫fillActiveViews()函式,該函式會把ListView中的所有ActiveViews 一次性都快取起來。

	/**
     * Fill ActiveViews with all of the children of the AbsListView.
     * ... ...
     */
    void fillActiveViews(int childCount, int firstActivePosition) {
        if (mActiveViews.length < childCount) {
            mActiveViews = new View[childCount];
        }
        mFirstActivePosition = firstActivePosition;

        //noinspection MismatchedReadAndWriteOfArray
        final View[] activeViews = mActiveViews;
		... ...
    }

而對於ScrapViews則是呼叫的addScrapView()函式。

	/**
     * Puts a view into the list of scrap views.
     * <p>
     * If the list data hasn't changed or the adapter has stable IDs, views
     * with transient state will be preserved for later retrieval.
     *
     * @param scrap The view to add
     * @param position The view's position within its parent
     */
    void addScrapView(View scrap, int position) {
    ... ...
    // Don't scrap views that have transient state.
        final boolean scrapHasTransientState = scrap.hasTransientState();
        if (scrapHasTransientState) {
        //Transient狀態
		... ...
		}else{
		//scrap狀態
		... ...
		}
		... ...
    }
    

該函式中又分為兩個不同的level,一個是transient瞬時態,另一個就是一般的普通態,關於這兩個狀態的區分我個人的想法是為了更加快速的獲取ScrapViews,因為處於瞬時狀態的view最有可能是接下來將要在介面上顯示的View,畢竟你向上或向下滑動列表時目的就是這個,這一點在obtainView()函式中得到了體現:

View obtainView(int position, boolean[] isScrap) {
... ...
//優先獲取TransientStateView
scrapView = mRecycler.getTransientStateView(position);
    if (scrapView == null) {
        scrapView = mRecycler.getScrapView(position);
    }
... ...
}

還有一個比較重要的函式就是scrapActiveViews()函式,它的作用是將目前所有的ActiveViews降級為ScrapViews,並將之前的所有ScrapViews清除。該函式在每次呼叫layoutChildern()函式時必定會被呼叫執行,目的就是為清空所有當前的ActiveViews,為新產生的ActiveViews做好準備。

    /**
     * Move all views remaining in mActiveViews to mScrapViews.
     */
    void scrapActiveViews() {
    ... ...
    //該函式確保mScrapViews的大小不會超過mActiveViews
    pruneScrapViews();
    }

結語

以上是閱讀了ListView以及AbListView原始碼後的一些心得總結,畢竟閱讀Android原始碼也才剛剛起步,還有很多地方理解的不是很透徹,上文若有理解不當之處歡迎各位指正。