1、 ViewHolder
1.1 作用
ViewHolder是对RecyclerView上的ItemView的封装,它是RecyclerView缓存的载体。它封装了以下属性:
- View itemView:对应RecyclerView的子View
- int mPosition:View当前对应数据在数据源中的位置
- int mOldPosition:View上次绑定的数据在数据源中的位置
- long mItemId:可以判断ViewHolder是否需要重新绑定数据
- int mItemViewType:itemView对应的类型
- int mPreLayoutPosition:在预布局阶段ViewHolder对应数据在数据源中的位置
- int mFlags:ViewHolder对应的标记位
- List mPayloads:实现局部刷新
- Recycler mScrapContainer:如果不为空,表示ViewHolder是存放在Scrap缓存中
1.2 flag
- FLAG_BOUND:ViewHolder对应的View已经绑定好了数据,无需重新绑定
- FLAG_UPDATE:数据发生了变化,View需要重新绑定
- FLAG_INVALID:数据失效了,View需要重新绑定
- FLAG_REMOVED:数据从数据源中删除,View在消失动画中仍然有用
- FLAG_NOT_RECYCLABLE:ViewHolder不能被回收,ViewHolder对应ItemView做动画时需要保证ViewHolder不能被回收掉
- FLAG_RETURNED_FROM_SCRAP:从scrap缓存中获取到的ViewHolder
- FLAG_IGNORE:如果回收该类型的ViewHolder会报错
- FLAG_TMP_DETACHED:表示ItemView从RecyclerView上DETACHED了,detach和remove的区别是,remove会将View从ViewGroup的children数组中删除并且刷新ViewGroup,detach只会删除不会触发刷新
- FLAG_ADAPTER_FULLUPDATE:表示ViewHolder需要全量更新,如果没有设置该标志位,则是局部更新
- FLAG_MOVED:当ViewHolder的位置发生变化,做动画时需要使用
- FLAG_APPEARED_IN_PRE_LAYOUT:ViewHolder出现在预布局中,需要做APPEARED动画
2、缓存架构
2.1 四级缓存
- ArrayList mAttachedScrap & ArrayListmChangedScrap
- ArrayList mCachedViews
- ViewCacheExtension mViewCacheExtension
- RecycledViewPool mRecyclerPool
由图可知,RecyclerView缓存是一个四级缓存的架构。当然,从RecyclerView的代码注释来看,官方认为只有三级缓存,即mCachedViews是一级缓存,mViewCacheExtension是二级缓存,mRecyclerPool是三级缓存。从开发者的角度来看,mAttachedScrap和mChangedScrap对开发者是不透明的,官方并未暴露出任何可以改变他们行为的方法。
public final class Recycler {
//1、Scrap缓存
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
//2、mCachedViews缓存
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;
//4、mRecyclerPool缓存
RecycledViewPool mRecyclerPool;
//3、mViewCacheExtension缓存
private ViewCacheExtension mViewCacheExtension;
........
}
2.2 scrap缓存
scrap缓存由mAttachedScrap和mChangedScrap两个缓存组成,在RecyclerView调用dispatchLayout时会使用该缓存,保存RecyclerView上的子View。
两部分组成:
- mAttachedScrap
- mChangedScrap
缓存特性:
- 对应的数据结构是ArrayList;
- 缓存大小没有限制,大小等于RecyclerView子View的个数;
- 该缓存中的ViewHolder无需重新绑定,只要ViewHolder的position和数据源中的position对应上了;
- 调用notifyItemRemoved、notifyItemMoved、notifyItemInserted方法,ViewHolder放入mAttachedScrap中;
- 调用notifyItemChanged(int position, Object payload),如果payload!=null ViewHolder放入mAttachedScrap中,否则ViewHolder放入mChangedScrap中;
- 调用notifyDataSetChanged()时,如果Adapter.hasStableIds返回true,ViewHolder放入mAttachedScrap中,否则会将ViewHolder回收到非scrap缓存中;
- LinearLayoutManager.layoutForPredictiveAnimations()阶段,mAttachedScrap数组剩下的ViewHolder是被挤出屏幕的.
2.3 mCachedViews缓存
缓存特性:
- 对应的数据结构是ArrayList;
- 缓存大小有限制,默认缓存大小为2,可以修改默认缓存大小。如果使用GridLayoutManager建议设置为列的个数;
- 该缓存中的ViewHolder无需重新绑定,只要ViewHolder的position和数据源中的position和itemType对应上了;
- 该缓存的特性是FIFO;
- ViewHolder mFlag如果有FLAG_INVALID、FLAG_REMOVED、FLAG_UPDATE、FLAG_ADAPTER_POSITION_UNKNOWN之一,不会放入该缓存;
- 当RecyclerView滑动时会将ViewHolder放入该缓存或者从该缓存获取ViewHolder.
mCacheViews可以通过如下方法,改变缓存的大小:
public void setItemViewCacheSize(int size) {
mRecycler.setViewCacheSize(size);
}
2.4 ViewCacheExtension
优化RecyclerView初始化时创建View对主线程阻塞的时长。
在空闲时刻进行RecyclerView部分item的初始化
2.5 RecyclerViewPool
缓存特性:
- 对应的数据结构是SparseArray,根据itemType将缓存分组,组的数据结构是ScrapData;
- ScrapData对应的数据结构是ArrayList,每个itemType对应的ScrapData的缓存大小默认值是5,可以修改缓存大小;
- 该缓存中的ViewHolder需要重新绑定数据;
- 可以提供给多个RecyclerView共享。
RecyclerViewPool缓存可以针对多ItemType,设置缓存大小。默认每个ItemType的缓存个数是5。而且该缓存可以给多个RecyclerView共享。由于默认缓存个数为5,假设某个新闻App,每屏幕可以展示10条新闻,那么必然会导致缓存命中失败,频繁导致创建ViewHolder影响性能。所以需要扩大缓存size。