Android中在寫列表的時候,相信不少時候,咱們都須要進行ListView嵌套ListView編程。好比說:帖子+評論頁面的編寫。然而這種模型是會出現被嵌套ListView緩存失效的問題。編程
被嵌套的ListView的代碼爲:緩存
public class NestListView extends ListView { public NestListView(Context context, AttributeSet attrs) { super(context, attrs); } public NestListView(Context context) { super(context); } public NestListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //無限大小的子View空間 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
最近,在寫微信朋友圈的功能中,由於須要處理說說和評論,**因此採用ListView 嵌套ListView的處理模式。**而不幸的是,發生了評論的ListView的View緩存沒法使用的狀況,從而形成GC比較頻繁。微信
CommentAdapter adapter = (CommentAdapter) lv_lc_comment.getAdapter(); if (adapter == null) { adapter = new CommentAdapter(context); lv_lc_comment.setAdapter(adapter); } //從新設置數據 adapter.setData(cmTopicCommentList); adapter.setClsId(cmTopic.getClsId()); adapter.setTag(tag); adapter.setOnClickListener(onTopicListener); adapter.notifyDataSetChanged();
上面這段代碼就是評論adapter刷新代碼。在實際運行過程當中,會發生View Cache沒法使用的問題。ide
調試代碼,開始是懷疑不能屢次調用setAdapter的問題**(以前都是沒有添加adapter是否存在判斷)**,而setAdapter代碼爲:調試
public void setAdapter(ListAdapter adapter) { ... mRecycler.clear(); ... }
能夠發現,setAdapter()會清空緩存。而採用最新的代碼編寫後,仍是發生了View Cache無效的問題。再次翻閱ListView的代碼AbsListView# obtainView :code
View obtainView(int position, boolean[] isScrap) { ... //查看是否存在相應的View緩存 scrapView = mRecycler.getScrapView(position); ... return child; }
可見,在ListView顯示的時候,會去查詢RecycleBin中是否存在對應的View緩存的,可是每次都是爲null。get
可是業務邏輯中,刷新一條說說的時候,會總體刷新它的評論,因此按照正常的邏輯來講,被嵌套的評論ListView中,應該包含了以前的評論View緩存。這必定是哪裏出了問題。it
因此看了一下**RecycleBin回收View的各個時機,**而後發現:io
protected void layoutChildren() { ... //若是數據變化,則回收View if (dataChanged) { for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); if (ViewDebug.TRACE_RECYCLER) { ViewDebug.trace(getChildAt(i), ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i); } } } else { recycleBin.fillActiveViews(childCount, firstPosition); } .... }
翻看了幾個addScrapView幾個被調用的地方後,發現layoutChildren這個調用最可疑,由於它利用dataChanged標誌來判斷是否回收View,而 notifyDataSetChanged就是標記須要回收View。class
而且ListView#onMeasure->ListView#measureHeightOfChildren() 須要進行獲取子View高度的時候:
final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, ... for (i = startPosition; i <= endPosition; ++i) { child = obtainView(i, isScrap); measureScrapChild(child, i, widthMeasureSpec); ... } ... }
**因此應該是NestListView這種無限長度的ListView的鍋:**由於在onMeasure的時候obtainView全部的View高度,而RecycleBin是在onLayout的時候才能緩存不使用的View。而View#onMeasure() > View#onLayout(), 因此形成被嵌套的ListView緩存失效的問題。
而最新的RecycleView已經能夠支持View強制回收了API爲:swapAdapter()。