探究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原始碼也才剛剛起步,還有很多地方理解的不是很透徹,上文若有理解不當之處歡迎各位指正。