1. RecyclerView复用机制流程:
以onTouchEvent作为切入口:
时序图中关键流程简要备注:
-
第1步, 代码切入点:
1)上下滑动会产生ViewHolder–>从OnTouchEvent()的MotionEvent.ACTION_MOVE事件处理;
2) 从onLayout作为切入口 -
第3步,mLayout.scrollVerticallyBy()中的mLayout指LayoutManager类对象,它是RecyclerView内部的 抽象类,LinearLayoutManager继承RecyclerView.LayoutManager,之后以上下滑动为例进行阐述;
-
第5步,fill()—>填充item,fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable)方法中的第一个参数RecyclerView.Recycler表示处理缓存(回收)复用 -
第7步,View view = layoutState.next(recycler);表示从缓存中拿到一个View进行复用处理,拿到view之后执行addView(view)添加到布局中。
-
第8步, recycler.getViewForPosition()–>根据位置拿到复用的View
-
第10步,tryGetViewHolderForPositionByDeadline这个非常重要 –》这个方法处理复用机制
-
第11步,getChangedScrapViewForPosition从mChangedScrap.get(i)(通过position和id进行获取)中去获取ViewHolder,mChangedScrap是一个ArrayList集合
-
第13步,如果从mChangedScrap集合中没有获取到,则利用getScrapOrHiddenOrCachedHolderForPosition方法通过
position
,首先从mAttachedScrap集合中,如果从mAttachedScrap集合中没获取到,再从mCachedViews集合中进行获取; -
第16步,如果从mCachedViews集合中没有获取到ViewHolder,则利用采用getScrapOrCachedViewForId通过
stable id
进行查找,流程跟第13步类似,首先从mAttachedScrap集合中没获取到,再从mCachedViews集合中进行获取; -
第19步,mViewCacheExtension是一个抽象类RecyclerView.ViewCacheExtension的对象实例, mViewCacheExtension.getViewForPositionAndType 这个接口其实是google工程师让用户自定义的,先自定义缓存,再自定义复用,这一步一般不要关,不会处理;
-
第20步,如果在第19步还是没获取到,就从缓存池中进行获取,调用getRecycledViewPool().getRecycledView()
-
第21步,如果从上面的流程中还没有获取到,就通过适配器创建一个ViewHolder,执行mAdapter.createViewHolder()流程,mAdapter指RecyclerView的内部类Adapter;createViewHolder()方法内部会执行onCreateViewHolder()方法,该方法是一个抽象方法
-
第23步,假如ViewHolder容器能够拿到,则会执行tryBindViewHolderByDeadline()方法进行绑定数据,tryBindViewHolderByDeadline()–>mAdapter.bindViewHolder()–> onBindViewHolder(),onBindViewHolder是一个抽象方法;
缓存和复用的对象是什么?–> ViewHolder –>可以简单理解为itemView
2. 四级缓存
-
mChangeScrap与mAttachedScrap
用来缓存还在屏幕内的ViewHolder
局部刷新,保存在mAttachedScrap中的直接拿来用,主要是性能优化处理
mChangeScrap主要是用于动画处理 -
mCachedViews –> 可以通过setMaxRecycledViews()更改默认大小
用来缓存移除屏幕之外的ViewHolder
ArrayList mCachedViews 默认大小 –> DEFAULT_CACHE_SIZE = 2 –> 可以通过setViewCacheSize()更改默认大小
ArrayList scrapHeap 默认大小 –> DEFAULT_CACHE_SIZE = 5 -
mViewCacheExtension
这个的创建和缓存完全由开发者自己控制,系统未往这里添加数据 -
RecycledViewPool
ViewHolder缓冲池
以onLayout作为切入口:
复用 是从 缓存中去取,先有缓存,在有复用。
3. 缓存
时序图中关键流程简要备注:
- 第2步,通过for循环不断获取缓存
-
第3步,scrapOrRecycleView()这个方法比较关键,分两者情况进行处理缓存
① recycler.recycleViewHolderInternal(viewHolder)
缓存到mCachedViews(默认大小2)和 RecycledViewPool中(默认大小5)。
② recycler.scrapView(view)
根据下面的条件来判断是缓存到mAttachedScrap 还是mChangedScrap中。—》这个就是一级缓存
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder))
- 第5步,如果mCacheView满了,取出ViewHolder放到根据viewType(第7步)放到RecycledViewPool(第6步)中,并将mCachedViews集合中第0个位置的remove;
mCachedViews和RecycledViewPool缓存流程:
mCacheView结构:队列
RecycledViewPool结构:有点类似hashMap
1)屏幕从下往上滑动,滑出1个ViewHolder(item),先放到mCacheView缓存中的index 为0的位置,如果屏幕在滑出1个item,放到index为1的位置;
2)如果屏幕此时在滑出一个ViewHolder,则将mCacheView缓存中index 为0的位置的ViewHolder,根据其ViewType,放到RecycledViewPool缓存对应的ScrapData中,ScrapData中最多只能放5个,超过5就return丢掉,如果没有满就会先将ViewHolder先置空(复位),再添加到ScrapData集合中;
3)将原来mCacheView缓存中index 为1的位置ViewHolder,放到mCacheView缓存中index 为0的位置,再将滑出屏幕的ViewHolder放到mCacheView缓存中index 为1的位置中。
onCreateViewHolder –> 缓存拿不到ViewHolder才会调用的,而不是拿不到数据
从缓存池中复用ViewHolder:需要调用 onBindViewHolder
从CacheView中复用ViewHolder:不用调用 onBindViewHolder
从缓存中没有拿到ViewHolder:会调用 onCreateViewHolder 和 onBindViewHolder
ListView与RecyclerView的区别:
ListView:只能在垂直方向滑动
RecyclerView:支持水平方向滑动,垂直方向滑动,多行多列瀑布流的方式等
ListView:有几个默认的Adapter,分别是ArrayAdapter、CursorAdapter和SimpleCursorAdapter
RecyclerView:Adapter需要自己实现
ListView:拥有子Item的监听函数:AdapterView.OnItemClickListener
RecyclerView:需要自己实现接口,来实现子Item的点击事件,虽然比较麻烦,但是扩展性好
ListView:并不强制使用ViewHolder,如果要使用,则需要自己定义,如果不使用,ListView每次getView()的时候都需要去findViewById,会造成性能下降,滑动卡顿等,所以推荐使用ViewHolder
RecyclerView:必须使用ViewHolder
ListView:两级缓存
1)mActiveViews 用于屏幕内ItemView快速重用
2)mScrapViews 用于缓存离开屏幕的ItemView
RecyclerView:四级缓存
1)mChangeScrap 与 mAttachedScrap 用于屏幕内ItemView 快速重用
2)mCachedViews 默认上限为2,即缓存屏幕外2个ItemView
3)mViewCacheExtension 用户自定义,一般不使用
4)RecycledViewPool 默认上限为5
缓存对象不同,RecyclerView缓存的是ViewHolder,ListView缓存的是View。