RecyclerView源码分析(⼀):Recycler
⽂章⽬录
前⾔
RecyclerView 是⼀个好⽤⼜复杂的控件,其功能的⾼度解耦化,规范化的 ViewHolder 写法,以及对动画的友好⽀持,都是它与传统ListView 的区别。
戚颖它有⼏⼤模块:
LayoutManager:控制 item 的布局
游乐场英文RecyclerView.Adapter:为 RecyclerView 提供数据
ItemDecoration:为 RecyclerView 添加分割线
ItemAnimator:控制 item 的动画
Recycler:负责回收和提供 View,和 RecyclerView 的复⽤机制相关
下⾯就从源码(API 28)⾓度分析 RecyclerView,RecyclerView 的源码很复杂,很难在⼀篇⽂章内讲完,所以打算分⼏篇来讲,本⽂是第⼀篇,将围绕 RecyclerView 的内部类 Recycler 展开分析:
RecyclerView.Recycler
⾸先看⼀下它的作⽤,源码上是这样写的:
A Recycler is responsible for managing scrapped or detached item views for reu.
意思就是 Recycler 负责管理废弃或被 detached 的 item 视图,以便重复利⽤。
roundtrip
它有以下⼏个成员变量:
主要成员变量
final ArrayList<ViewHolder> mAttachedScrap =new ArrayList<>();
where是什么意思中文
ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews =new ArrayList<ViewHolder>();
RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
这⼏个成员变量都和 RecyclerView 的缓存相关,如果按照四级缓存的话,它们可以这样划分:
第⼀级缓存:mAttachedScrap、mChangedScrap
第⼆级缓存:mCachedViews
第三级缓存:ViewCacheExtension
第四级缓存:RecycledViewPool
后⾯再介绍 mAttachedScrap、mChangedScrap、mCachedViews 具体存的是哪些 ViewHolder。
现在先了解下 RecycledViewPool 和 ViewCacheExtension这两个类:
RecycledViewPool
继续先看官⽅注释:
RecycledViewPool lets you share Views between multiple RecyclerViews.
RecycledViewPool ⽤于在多个 RecyclerView 间共享 View。
在使⽤时,只需创建 RecycledViewPool 实例,然后调⽤ RecyclerView 的 tRecycledViewPool(RecycledViewPool) ⽅法即可。RecycledViewPool 存储在 Recycler 中,通过 Recycler 存取。
成员变量
RecycledViewPool 有⼀个重要的成员变量:
// SparArray 类似于 key 为 int 类型的 HashMap
SparArray<ScrapData> mScrap =new SparArray<>();
其中 ScrapData 的定义如下:
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap =new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;// 5
long mCreateRunningAverageNs =0;
long mBindRunningAverageNs =0;
}
mScrap 是⼀个 <int, ScrapData> 的映射,其中 int 代表了 viewType,ScrapData 则存储了⼀个 ViewHolder 集合。
主要⽅法
getScrapDataForType
private ScrapData getScrapDataForType(int viewType){
ScrapData scrapData = (viewType);
if(scrapData == null){
scrapData =new ScrapData();
mScrap.put(viewType, scrapData);
}
return scrapData;
}
该⽅法根据 viewType 获取相应的 ScrapData,如果该 viewType 还没有绑定 ScrapData,就新创建⼀个 ScrapData 并绑定到该viewType。
tMaxRecycledViews
public void tMaxRecycledViews(int viewType,int max){
ScrapData scrapData =getScrapDataForType(viewType);
scrapData.mMaxScrap = max;
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
// 从后⾯开始删除,直到满⾜新的容量
while(scrapHeap.size()> max){
}
}
该⽅法可以设置相应 viewType 的 View 容量,超出容量时,从后⾯开始删除,直到满⾜新的容量。getRecycledView
public ViewHolder getRecycledView(int viewType){
final ScrapData scrapData = (viewType);
if(scrapData != null &&!scrapData.mScrapHeap.isEmpty()){
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
ve(scrapHeap.size()-1);
}
return null;
}
该⽅法根据 viewType 获取⼀个 ViewHolder,获取到的 ViewHolder 将会被移除出 Scrap 堆。获取不到则返回 null。putRecycledView
public void putRecycledView(ViewHolder scrap){
final int viewType = ItemViewType();
final ArrayList<ViewHolder> scrapHeap =getScrapDataForType(viewType).mScrapHeap;
// 容量已满,不再添加
(viewType).mMaxScrap <= scrapHeap.size()){
return;
}
// 重置 ViewHolder,例如清空 flags
// 将该 ViewHolder 添加到对应 viewType 的集合中缓存起来
scrapHeap.add(scrap);
}
该⽅法也很好理解,根据 ViewHolder 的 viewType 放⼊ RecycledViewPool 的相应集合中,如果集合已满,不再添加。
接下来看另⼀个类:
ViewCacheExtension
ViewCacheExtension 是⼀个由开发者控制的 View 缓存帮助类,其定义如下:不可能的英文
public abstract static class ViewCacheExtension {
/**
* Returns a View that can be binded to the given Adapter position.
*/
@Nullable
public abstract View getViewForPositionAndType(@NonNull Recycler recycler,int position,
int type);
}
开发者可以实现这个抽象类,通过调⽤ RecyclerView 的 tViewCacheExtension(ViewCacheExtension) ⽅法设置,最终将ViewCacheExtension 存储在 Recycler 中。
当调⽤ Recycler 的 getViewForPosition ⽅法时,如果 attached scrap 和 已经缓存都没有找到合适的 View,就会调⽤ViewCacheExtension 的 getViewForPositionAndType ⽅法来获取 View。
需要注意的是,Recycler 不会对这个类做任何缓存处理,是否需要缓存 View 由开发者⾃⼰控制。
主要⽅法
看完这两个类,现在回到 Recycler 中,看⼀下 Rcycler 的主要⽅法:
getViewForPosition
getViewForPosition ⽅法⽐较重要,⽤于获取某个位置需要展⽰的 View,如下:
public View getViewForPosition(int position){
return getViewForPosition(position,fal);
}
View getViewForPosition(int position,boolean dryRun){
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
继续看 tryGetViewHolderForPositionByDeadline ⽅法,该⽅法会依次从⼏个缓存中获取,分别来看⼀下:
// 如果是处于预布局阶段(先简单理解为执⾏ dispatchLayoutStep1 ⽅法)
// (其实下⾯⽅法要返回 ture 还需要开启“预处理动画”,这跟动画有关,先不多说)英语面试口语900句
if(mState.isPreLayout()){
holder =getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
第⼀步,从 mChangedScrap 中获取,获取不到就返回 null。
remember to do和doing区别如果 holder 还是为 null,执⾏下⾯代码:
// 1) Find by position from scrap/hidden list/cache
if(holder == null){
holder =getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
if(holder != null){
if(!validateViewHolderForOfftPosition(holder)){
/
/ recycle holder (and unscrap if relevant) since it can't be ud
// 回收⽆效的 ViewHolder
// ...
英文手机铃声排行榜}el{
fromScrapOrHiddenOrCache =true;
}
}
}
第⼆步,根据 position 依次从 mAttachedScrap、mHiddenViews(存储在 ChildHelper 类)、mCachedViews 中获取缓存的ViewHolder。
可以从 mHiddenViews 获取到缓存的话,就将其从 mHiddenViews 移除并添加到 Scrap 缓存(根据情况添加到 mAttachedScrap 或mChangedScrap)。可以从 mCacheViews 中获取到缓存的话,就将
其从 mCacheViews 移除。
获取到后,发现⽆效的话,将对获取到的 ViewHolder 进⾏清理并回收(放⼊ mCachedViews 或 RecycledViewPool)。
获取不到,就继续往下执⾏:
// 默认返回 fal,可通过 Adapter.tHasStableIds ⽅法设置该值
if(mAdapter.hasStableIds()){
// 根据 id 依次在 mAttachedScrap、mCachedViews 中获取缓存
holder =ItemId(offtPosition),
type, dryRun);
if(holder != null){
holder.mPosition = offtPosition;
fromScrapOrHiddenOrCache =true;
}
}
第三步,根据 id 依次从 mAttachedScrap、mCachedViews 中获取缓存,还没有获取到就继续往下:
// 如果⽤户设置了 ViewCacheExtension
if(holder == null && mViewCacheExtension != null){
// We are NOT nding the offtPosition becau LayoutManager does not
// know it.
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if(view != null){
qbqholder =getChildViewHolder(view);
/
/ ...
}
}
第四步,从⽤户设置的 ViewCacheExtension 中获取缓存,没有获取到就继续往下:
if(holder == null){// fallback to pool
holder =getRecycledViewPool().getRecycledView(type);
// ...
}
第五步,根据 viewType 从 RecycledViewPool 中得到缓存。
RecycledViewPool 已经是最后⼀级缓存了,如果这⾥也没有获取到,只能通过 Adapter 的 createViewHolder ⽅法创建⼀个ViewHolder:
if(holder == null){
holder = ateViewHolder(RecyclerView.this, type);
// ...
}
最后⼩结⼀下获取某个位置的 View 的过程:
1. 先后根据 position 或 id 从 mChangedScrap 中获取缓存
2. 根据 position 依次从 mAttachedScrap、mHiddenViews(存储在 ChildHelper 类)、mCachedViews 中获取缓存
3. 根据 id 依次从 mAttachedScrap、mCachedViews 中获取缓存
4. 从⽤户设置的 ViewCacheExtension 中获取缓存
5. 从 RecycledViewPool 中得到缓存的废弃 ViewHolder
6. 通过 Adapter 的 createViewHolder ⽅法创建⼀个 ViewHolder
recycleView
既然叫 Recycler,那肯定要做回收⼯作了,recycleView ⽅法就完成了这些⼯作,下⾯看⼀下该⽅法的实现:
public void recycleView(@NonNull View view){
ViewHolder holder =getChildViewHolderInt(view);
// ...
recycleViewHolderInternal(holder);
}
继续看 recycleViewHolderInternal:
mojito什么意思