小葵花妈妈课堂开课了《RecyclerView 复用解析》
最新項(xiàng)目遇到一個(gè)問題,就是RecycleView的itemview會頻繁拉取圖片,同一時(shí)間多次拉取同一張照片。
初探,是因?yàn)樵搱鼍皀otifyDataSetChanged()過于頻繁,一秒鐘會調(diào)用5次左右,
導(dǎo)致ViewHolder沒有復(fù)用,也不是沒有復(fù)用而是復(fù)用的并沒有像理想中的樣式。
notify
4.1.1 markKnownViewsInvalid
//1 Mark all known views as invalid,僅標(biāo)記可見的布局為ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
//2 mAdapter.hasStableIds() true -> mCachedViews 標(biāo)記 ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
//3 mAdapter.hasStableIds() false -> addViewHolderToRecycledViewPool ->getRecycledViewPool().putRecycledView(holder); AND mCachedViews.clear();
// we cannot re-use cached views in this case. Recycle them all
4.1.2 markItemDecorInsetsDirty
//child.getLayoutParams()).mInsetsDirty = true;
//mCachedViews layoutParams.mInsetsDirty = true;
回收邏輯
6.1 viewHolder.isInvalid() && !viewHolder.isRemoved() && !mRecyclerView.mAdapter.hasStableIds() -->
6.1.1 true recycler.recycleViewHolderInternal(viewHolder);
addViewHolderToRecycledViewPool(holder, true); //最終放入getRecycledViewPool()
6.1.2 false scrapView(View view)
holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID) || !holder.isUpdated() || canReuseUpdatedViewHolder(holder) -->
6.1.2.1 true mAttachedScrap.add(holder);
6.1.2.2 false mChangedScrap.add(holder);
總結(jié):如果Holder無效最終放入getRecycledViewPool,否則mAttachedScrap
回收另外一個(gè)分支
vh.setIsRecyclable(true);
forceRecycle || holder.isRecyclable() 成立
(mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) 不成立,包含ViewHolder.FLAG_INVALID
查找可復(fù)用Holder過程
RecyclerView.State state, boolean stopOnFocusable)
LayoutState layoutState, LayoutChunkResult result)
9.1 isPreLayout() --> getChangedScrapViewForPosition(int position) //如果是pre-layout,會從mChangedScrap獲取復(fù)用
9.2 getScrapOrHiddenOrCachedHolderForPosition(position, dryRun)
9.2.1 mAttachedScrap 首先從該處獲取,
9.2.2 mChildHelper.findHiddenNonRemovedView(position) 拿到隱藏的ViewHolder
9.2.3 mCachedViews //Search in our first-level recycled view cache. 最后從一級緩存獲取
// invalid view holders may be in cache if adapter has stable ids as they can be
// retrieved via getScrapOrCachedViewForId
// 通過原廠注釋可以知道,has stable才可以使用mCachedViews緩存。在回收處也可以看出。
9.2.4 mAdapter.hasStableIds() --> getScrapOrCachedViewForId() //仍然需要hasStableIds
// 首先mAttachedScrap
// 之后mCachedViews
9.3 mViewCacheExtension //外部擴(kuò)展
9.4 getRecycledViewPool().getRecycledView(type) //此處查到holder被resetInternal,即需要重新bind
9.5 mAdapter.createViewHolder(RecyclerView.this, type); //最后,create
bind過程
boolean dryRun, long deadlineNs)
至此RecyclerView的復(fù)用機(jī)制已經(jīng)差不多了。至此也看到了我的問題也出現(xiàn)了答案的線索。
因?yàn)槭莕otifyDataSetChanged出發(fā)的刷新,會將所有ViewHolder標(biāo)記為FLAG_INVALID。
在回收ViewHolder時(shí),會放入getRecycledViewPool中。
RecycledViewPool官方注釋
RecycledViewPool lets you share Views between multiple RecyclerViews.
If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
RecyclerView automatically creates a pool for itself if you don’t provide one.
作用為多個(gè)RecyclerViews之間復(fù)用ViewHolder。
可以通過setRecycledViewPool進(jìn)行設(shè)置。
SparseArray mScrap = new SparseArray<>(); 用來存儲不同類型的ViewHolder
默認(rèn)mMaxScrap大小為5個(gè)
private static final int DEFAULT_MAX_SCRAP = 5;也就是做多存儲5個(gè)。我的項(xiàng)目正好有6個(gè)Item顯示,就導(dǎo)致只能存儲 1,2,3,4,5 而0沒有進(jìn)行存儲
onBind的時(shí)候就會出現(xiàn)0 - 綁定 1,1 綁定2… 的問題。
解決辦法為通過
new一個(gè)RecyclerView.RecycledViewPool, 并設(shè)置recycledViewPool.setMaxRecycledViews(0, 6);
第一個(gè)參數(shù)viewType,我這里僅有一種type,所以就寫0
最終通過mRecycleList.setRecycledViewPool(recycledViewPool); 設(shè)置RecycledViewPool。
至此大工告成。
RecyclerView源碼有1w多行。剛看的時(shí)候無從下手,向其他博主所說的見森林而不見樹木。
我就是從notifyDataSetChanged 和 onBindViewHolder入手,查找上下邏輯從而追蹤定位問題。
總結(jié)
以上是生活随笔為你收集整理的小葵花妈妈课堂开课了《RecyclerView 复用解析》的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nginx的带宽限制和并发控制
- 下一篇: 红桃3