衆所周知,RecyclerView在android中實作清單是性能非常好的,那麼性能好的原因在哪裡呢?關鍵還是在它在處理view時的回收和複用。清單在滑動的時候,會進行itemView的回收和複用,那麼我們就從滑動回調即onTouchEvent來入手分析。
基本概念
- ViewHolder: View的容器,一項View就對應一個ViewHolder
- Recyler:RecyclerView的内部類,主要負責View的回收和複用
- LinearLayoutManager: RecyclerView的線性布局管理器
滑動時函數調用鍊

四級緩存機制
層級 | 緩存變量 | 緩存名 | 容量 | 資料結構 | 緩存用途 |
---|---|---|---|---|---|
1 | mChangeScrap與 mAttachedScrap | 可見緩存 | x | ArrayList | 用于布局過程中螢幕可見表項的回收和複用 |
2 | mCachedViews | 緩存清單 | 2 | ArrayList | 用于移出螢幕表項的回收和複用,不會清空資料 |
3 | mViewCacheExtension | 自定義緩存 | x | ||
4 | RecycledViewPool | 緩存池 | 5 | SparseArray | 用于移出螢幕表項的回收和複用,會将ViewHolder的資料重置 |
回收的關鍵方法分析
## RecyclerView.java
void recycleViewHolderInternal(ViewHolder holder) {
...
if (forceRecycle || holder.isRecyclable()) {
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN))
// 1) 先嘗試放到cacheView中
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
// 如果 mCachedViews 已經滿了,把第0個位置的移除并放到 緩存池中
recycleCachedViewAt(0);
cachedViewSize--;
}
if (!cached) {
// 2) 如果CacheView中沒放進去,就放到 緩存池中
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
...
}
}
複用的關鍵方法分析
tryGetViewHolderForPositionByDeadline
- 從一級緩存 mChangeScrap 中取
- 從二級緩存 mCachedViews中取
- 從三級緩存 mViewCacheExtension 中取
- 從四級緩存 緩存池中取
- 緩存中都沒有拿到值,就直接建立
- 未綁定過時,進行綁定
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
ViewHolder holder = null;
// 1) 從一級緩存 changed scrap 中取
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 2)從二級緩存 cache中取
if (holder == null) {
final int type = mAdapter.getItemViewType(offsetPosition);
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
if (holder != null) {
// update position
holder.mPosition = offsetPosition;
fromScrapOrHiddenOrCache = true;
}
}
// 3. 從三級緩存 CacheExtension 中取
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
}
// 4) 從四級緩存 緩存池中取
if (holder == null) { // fallback to pool
holder = getRecycledViewPool().getRecycledView(type);
if (holder != null) {
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
// 5)緩存中都沒有拿到值,就直接建立
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
if (ALLOW_THREAD_GAP_WORK) {
// only bother finding nested RV if prefetching
RecyclerView innerView = findNestedRecyclerView(holder.itemView);
if (innerView != null) {
holder.mNestedRecyclerView = new WeakReference<>(innerView);
}
}
long end = getNanoTime();
mRecyclerPool.factorInCreateTime(type, end - start);
if (DEBUG) {
Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
}
}
}
// 6)已經 bind過了,不會再去綁定,未綁定過時,進行綁定
if (mState.isPreLayout() && holder.isBound()) {
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
// 嘗試 bindView
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
return holder;
}
一級緩存-緩存碎片
ViewHolder getChangedScrapViewForPosition(int position) {
// If pre-layout, check the changed scrap for an exact match.
final int changedScrapSize;
if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
return null;
}
// find by position
for (int i = 0; i < changedScrapSize; i++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
// find by id
if (mAdapter.hasStableIds()) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
final long id = mAdapter.getItemId(offsetPosition);
for (int i = 0; i < changedScrapSize; i++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
}
}
return null;
}
二級緩存-緩存清單
ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
// 1) 先從mAttachedScrap中取,取到便傳回
final int count = mAttachedScrap.size();
for (int i = count - 1; i >= 0; i--) {
final ViewHolder holder = mAttachedScrap.get(i);
if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
if (type == holder.getItemViewType()) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
if (holder.isRemoved()) {
if (!mState.isPreLayout()) {
holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
}
}
return holder;
} else if (!dryRun) {
mAttachedScrap.remove(i);
removeDetachedView(holder.itemView, false);
quickRecycleScrapView(holder.itemView);
}
}
}
// 2)二級緩存,從mCachedViews中取
final int cacheSize = mCachedViews.size();
for (int i = cacheSize - 1; i >= 0; i--) {
final ViewHolder holder = mCachedViews.get(i);
//從mCachedViews中取到後便傳回
if (holder.getItemId() == id) {
if (type == holder.getItemViewType()) {
if (!dryRun) {
mCachedViews.remove(i);
}
return holder;
} else if (!dryRun) {
recycleCachedViewAt(i);
return null;
}
}
}
return null;
}
三級緩存-自定義緩存
略
四級緩存-緩存池
public ViewHolder getRecycledView(int viewType) {
//從mScrap中根據view的類型來取出一個
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
//從 scrapData 中拿最後一個資料,先進後出
return scrapHeap.remove(scrapHeap.size() - 1);
}
return null;
}
static class ScrapData {
//ViewHolder是作為一個List被存進來的
ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
// 緩存池中 list的大小是5,也就是每個類型的view緩存池中存儲5個
int mMaxScrap = 5;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}