一:前言
RecyclerView是我們開發(fā)中很常用的一個(gè)控件,但是阿簡沒有仔細(xì)的去了解以下其實(shí)現(xiàn)原理杯矩,最近正好在搞一個(gè)需求,看了下源碼,一起學(xué)習(xí)下~
二:關(guān)于RecyclerView
在我們使用RecyclerView的時(shí)候不免使用Adapter去處理數(shù)據(jù)渐排,處理完了數(shù)據(jù)之后再recyclerView.setAdapter(_)去顯示相關(guān)item,可以看出灸蟆,UI和數(shù)據(jù)是分開的驯耻,通過對recyclerView源碼的學(xué)習(xí),recyclerView在設(shè)計(jì)的時(shí)候也遵循這個(gè)規(guī)則
三:RecyclerView.class中Recycler內(nèi)部類說明
Recycler內(nèi)部類:
mAttachedScrap:緩存當(dāng)前屏幕可見的ViewHolder
mChangedScrap:ViewHolder更新的時(shí)候會保存在這個(gè)ArrayList里面
mCachedViews:緩存滑動時(shí)即將與RecyclerView分離的ViewHolder,按子View的position或id緩存可缚,默認(rèn)最多存放2個(gè)
mViewCacheExtension:用戶自定義緩存
mRecyclerPool:緩存池(后面說明)
public final class Recycler {
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private final List<ViewHolder>
mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
int mViewCacheMax = DEFAULT_CACHE_SIZE;
RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
static final int DEFAULT_CACHE_SIZE = 2;
四:什么時(shí)候會使用到緩存機(jī)制霎迫?入口在哪?
4.1:
RecyclerView是一個(gè)自定義viewGroup城看,所以我們從onTouchEvent(MotionEvent event)女气,當(dāng)event等于MotionEvent.ACTION_MOVE就是滑動的時(shí)候開始分析:
case MotionEvent.ACTION_MOVE: {
...省略
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
vtev)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (mGapWorker != null && (dx != 0 || dy != 0)) {
mGapWorker.postFromTraversal(this, dx, dy);
}
}
} break;
我們來看一下從scrollByInternal()一直點(diǎn)進(jìn)去之后的調(diào)用鏈
OnTouch --> scrollByInternal --> scrollStep --> mLayout.scrollVerticallyBy --> scrollBy --> fill --> layoutChunk --> layoutState.next --> getViewForPosition --> tryGetViewHolderForPositionByDeadline
4.2:
當(dāng)RecyclerView中item發(fā)生變化的時(shí)候,就是Adapter調(diào)用了notifydatasetchanged這些一系列的方法時(shí)候测柠,會觸發(fā)以下調(diào)用鏈:
notifyDataSetChanged-->mObservable.notifyChanged --> (RecyclerViewDataObserver)mObservers.get(i).onChanged --> requestLayout
可以看到最后調(diào)用到了requestLayout炼鞠,所以會重新執(zhí)行RecyclerView的onMeasure-onLayout-onDraw方法,然后就會開始調(diào)用緩存操作:
onMeasure()/onLayout() -->dispatchLayoutStep1()/dispatchLayoutStep2()--> mLayout.onLayoutChildren -->onLayoutChildren() -->fill()->layoutChunk() -->next() -->getViewForPosition()-->tryGetViewHolderForPositionByDeadline
五:緩存機(jī)制分析
由第四步可知最后都是調(diào)用到了fill()->layoutChunk() -->next() -->getViewForPosition()-->tryGetViewHolderForPositionByDeadline
那我們來看tryGetViewHolderForPositionByDeadline做了什么(這個(gè)方法也是我們常說的四級緩存的實(shí)現(xiàn))
//根據(jù)傳入的position獲取ViewHolder
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
...省略
boolean fromScrapOrHiddenOrCache = false;
ViewHolder holder = null;
//預(yù)布局 屬于特殊情況 從mChangedScrap中獲取ViewHolder
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
if (holder == null) {
//1轰胁、嘗試從mAttachedScrap中獲取ViewHolder,此時(shí)獲取的是屏幕中可見范圍中的ViewHolder
//2谒主、mAttachedScrap緩存中沒有的話,繼續(xù)從mCachedViews嘗試獲取ViewHolder
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
...省略
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
...省略
final int type = mAdapter.getItemViewType(offsetPosition);
//如果Adapter中聲明了Id赃阀,嘗試從id中獲取霎肯,這里不屬于緩存
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
}
if (holder == null && mViewCacheExtension != null) {
3、從自定義緩存mViewCacheExtension中嘗試獲取ViewHolder榛斯,該緩存需要開發(fā)者實(shí)現(xiàn)
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
}
if (holder == null) { // fallback to pool
//4观游、從緩存池mRecyclerPool中嘗試獲取ViewHolder
holder = getRecycledViewPool().getRecycledView(type);
if (holder != null) {
//如果獲取成功,會重置ViewHolder狀態(tài)驮俗,所以需要重新執(zhí)行Adapter#onBindViewHolder綁定數(shù)據(jù)
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
if (holder == null) {
...省略
//5懂缕、若以上緩存中都沒有找到對應(yīng)的ViewHolder,最終會調(diào)用Adapter中的onCreateViewHolder創(chuàng)建一個(gè)
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
}
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
//6王凑、如果需要綁定數(shù)據(jù)搪柑,會調(diào)用Adapter#onBindViewHolder來綁定數(shù)據(jù)
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
...省略
return holder;
}
可見以下流程圖:
六:RecyclerView.class中RecycledViewPool內(nèi)部類說明
通過上一步的分析可知,前面三級緩存是不用重新綁定viewHolder的索烹,只有從RecycledViewPool中獲取到的RecycledViewPool需要重新綁定數(shù)工碾。
通過下面一種場景可以幫助理解:如一個(gè)APP的主界面ViewPager有多個(gè)fragment,其中某兩個(gè)fragment都會用到RecyclerView,而且其中的一些item還是相同的百姓,那么此時(shí)我們?yōu)榱斯?jié)約內(nèi)存渊额,就可以使用
RecycledViewPool讓這兩個(gè)RecyclerView共享同一個(gè)四級緩存pool
RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
MyAdapter myAdapter = new MyAdapter(datas);
RecyclerView.ViewHolder viewHolder = myAdapter.createViewHolder(recyclerView, 0);
pool.putRecycledView(viewHolder);
recyclerView.setRecycledViewPool(pool);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(myAdapter);