RecyclerView缓存分析
来源:互联网 发布:网络代购图片 编辑:程序博客网 时间:2024/06/05 10:21
缓存介绍
public final class Recycler { final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>(); ArrayList<ViewHolder> mChangedScrap = null; final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();// mAttachedScrap的不可变视图 private final List<ViewHolder> mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);// 预缓存数,设置后会相应更新mViewCacheMax的值 private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;// 最大缓存数 int mViewCacheMax = DEFAULT_CACHE_SIZE; RecycledViewPool mRecyclerPool; private ViewCacheExtension mViewCacheExtension; static final int DEFAULT_CACHE_SIZE = 2;}RecyclerView拥有三级缓存(算上mAdapter.createViewHolder的话其实就有四级了),我们先看下各个缓存变量的用处。之后会分析RecyclerView是如何使用这些缓存的。
获取缓存
对于多级缓存的使用我们来看下
tryGetViewHolderForPositionByDeadline
方法ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { if (position < 0 || position >= mState.getItemCount()) { throw new IndexOutOfBoundsException("Invalid item position " + position + "(" + position + "). Item count:" + mState.getItemCount()); } boolean fromScrapOrHiddenOrCache = false; ViewHolder holder = null; // 0) If there is a changed scrap, try to find from there if (mState.isPreLayout()) { // 从mChangedScrap中获取ViewHolder holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; } // 1) Find by position from scrap/hidden list/cache if (holder == null) { // 从mAttachedScrap 与 mCachedViews中获取ViewHolder holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); if (holder != null) { if (!validateViewHolderForOffsetPosition(holder)) { // recycle holder (and unscrap if relevant) since it can't be used if (!dryRun) { // we would like to recycle this but need to make sure it is not used by // animation logic etc. // FLAG_INVALID 回收标记 holder.addFlags(ViewHolder.FLAG_INVALID); if (holder.isScrap()) { removeDetachedView(holder.itemView, false); holder.unScrap(); } else if (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } } if (holder == null) { final int offsetPosition = mAdapterHelper.findPositionOffset(position); if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item " + "position " + position + "(offset:" + offsetPosition + ")." + "state:" + mState.getItemCount()); } final int type = mAdapter.getItemViewType(offsetPosition); // 2) Find from scrap/cache via stable ids, if exists if (mAdapter.hasStableIds()) { // 通过id查找ViewHolder holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun); if (holder != null) { // update position holder.mPosition = offsetPosition; fromScrapOrHiddenOrCache = true; } } if (holder == null && mViewCacheExtension != null) { // We are NOT sending the offsetPosition because LayoutManager does not // know it. // 如果使用者定义了mViewCacheExtension,则从中开始查找ViewHolder final View view = mViewCacheExtension .getViewForPositionAndType(this, position, type); if (view != null) { holder = getChildViewHolder(view); if (holder == null) { throw new IllegalArgumentException("getViewForPositionAndType returned" + " a view which does not have a ViewHolder"); } else if (holder.shouldIgnore()) { throw new IllegalArgumentException("getViewForPositionAndType returned" + " a view that is ignored. You must call stopIgnoring before" + " returning this view."); } } } if (holder == null) { // fallback to pool if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline(" + position + ") fetching from shared pool"); } // 从mRecycledViewPool中查找 holder = getRecycledViewPool().getRecycledView(type); if (holder != null) { holder.resetInternal(); if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } } if (holder == null) { long start = getNanoTime(); if (deadlineNs != FOREVER_NS && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) { // abort - we have a deadline we can't meet return null; } // 上面那么多缓存都没找到的话,就创建一个 holder = mAdapter.createViewHolder(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"); } } } // This is very ugly but the only place we can grab this information // before the View is rebound and returned to the LayoutManager for post layout ops. // We don't need this in pre-layout since the VH is not updated by the LM. if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) { holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST); if (mState.mRunSimpleAnimations) { int changeFlags = ItemAnimator .buildAdapterChangeFlagsForAnimations(holder); changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT; final ItemAnimator.ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState, holder, changeFlags, holder.getUnmodifiedPayloads()); recordAnimationInfoIfBouncedHiddenView(holder, info); } } boolean bound = false; if (mState.isPreLayout() && holder.isBound()) { // do not update unless we absolutely have to. holder.mPreLayoutPosition = position; } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { if (DEBUG && holder.isRemoved()) { throw new IllegalStateException("Removed holder should be bound and it should" + " come here only in pre-layout. Holder: " + holder); } final int offsetPosition = mAdapterHelper.findPositionOffset(position); // 调用mAdapter.bindViewHolder绑定ViewHolder bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); } final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); final LayoutParams rvLayoutParams; if (lp == null) { rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); holder.itemView.setLayoutParams(rvLayoutParams); } else if (!checkLayoutParams(lp)) { rvLayoutParams = (LayoutParams) generateLayoutParams(lp); holder.itemView.setLayoutParams(rvLayoutParams); } else { rvLayoutParams = (LayoutParams) lp; } rvLayoutParams.mViewHolder = holder; rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound; return holder;}
缓存池
现在来看看RecycledViewPool的实现。
public static class RecycledViewPool { private static final int DEFAULT_MAX_SCRAP = 5;static class ScrapData { ArrayList<ViewHolder> mScrapHeap = new ArrayList<>(); int mMaxScrap = DEFAULT_MAX_SCRAP; long mCreateRunningAverageNs = 0; long mBindRunningAverageNs = 0;}SparseArray<ScrapData> mScrap = new SparseArray<>(); /** * 从缓存池获取一个ViewHolder,并从缓存池中删除 */ public ViewHolder getRecycledView(int viewType) { final ScrapData scrapData = mScrap.get(viewType); if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) { final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap; return scrapHeap.remove(scrapHeap.size() - 1); } return null; } /** * viewType对应的中ScrapData添加ViewHolder */ public void putRecycledView(ViewHolder scrap) { final int viewType = scrap.getItemViewType(); final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap; if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) { return; } if (DEBUG && scrapHeap.contains(scrap)) { throw new IllegalArgumentException("this scrap item already exists"); } scrap.resetInternal(); scrapHeap.add(scrap); } /** * 根据viewType获取ScrapData */ private ScrapData getScrapDataForType(int viewType) { ScrapData scrapData = mScrap.get(viewType); if (scrapData == null) { scrapData = new ScrapData(); mScrap.put(viewType, scrapData); } return scrapData; }}
RecycledViewPool是由SparseArray实现,其中包含了多个viewType对应的ArrayList集合。获取时通过viewType得到对应的ArrayList集合,之后返回一个ViewHolder,并在集合中删除这个ViewHolder。添加操作也是类似,只不过在发现没有viewType对应的ArrayList集合时,将进行创建,并且在比较大小不超过默认大小5时,添加进入ArrayList。
总结
本文分析了RecyclerView的缓存实现,其通过多级缓存的方式实现了ViewHolder的重用,减少了ViewHolder创建,提升了效率
1 0
- RecyclerView缓存分析
- RecyclerView分析
- RecyclerView缓存的问题
- RecyclerView 缓存机制详解
- RecyclerView缓存机制总结
- Android viewHolder缓存RecyclerView,ListView
- RecyclerView源码分析
- RecyclerView源码分析
- RecyclerView滑动源码分析
- Android RecyclerView源码分析
- RecyclerView机制分析: Recycler
- RecyclerView机制分析: State
- RecyclerView源码分析
- recyclerview的应用分析
- RecyclerView整体思路分析
- RecyclerView源码分析
- RecyclerView源码分析
- RecyclerView源码分析
- Linux中shell编程需要避免的坑之shell语法篇(三)
- android连接mysql数据库,NetworkOnMainThreadException引起CommunicationsException
- 深度学习算法实践9---用Theano实现多层前馈网络
- 一份不太简短的LaTeX模板
- Window7下安装openssl完整版(亲测实现)
- RecyclerView缓存分析
- 分享几个有意思的API接口
- ubuntu安装mysql数据库遇到的问题及解决办法
- JBoss4 多数据源配置,异常处理
- 修改Ubuntu的aptget源为阿里源的方法
- 在windows下使用Xming+Putty显示Linux下软件图形界面
- 关于gettimeofday函数的使用问题
- 工作常用操作
- 深度学习算法实践10---卷积神经网络(CNN)原理