缓存是 RecyclerView 时间功用优胜的重要原因。缓存池是悉数缓存中速度最慢的,其间的ViewHodler是脏的,得从头施行onBindViewHolder()。这一篇从源码动身appreciate,根究哪些情况下“表项会被收回到缓存池”。

缓存池结构

在剖析不同的收回场景前,先回想approach一下“缓存池是什么?”

表项被收回到缓存池,在源码上的表项为 ViewHolder 实例被存储到Recycle缓存兼并东西m3u8dViewPool结构中:

public class RecyclerVie缓存w {
public final class Recycler {
// 收回表项视图
public void recycledenon是什么品牌View(@NonNull View view) {
VappleidiewHolder holder = getCh狗狗币ildViewHolderInt(view);
// 收回表项 ViewHolder
recydeno是什么意思cleViewHolderInternalapp装置下载(holder);
}
// 收回 ViewHolder
void recycleViewHo面试需求预备什么lderInternalappstore(Vie缓存视频兼并软件wHolder holder) {
...
// 将 ViewHolder 存入缓存池
addViewHolderToRecycledViewPool(holder, true);枸杞
}
// 将 ViewHold作业细胞er 实例存储到 RecycledViewPool 结构中
void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
...
getRecycledViewPool().putRecycled缓存视频在手机哪里找View(holder);
}
// 获取 RecycledViewPool狗狗币 实例
RecycledViewPool getRecycleappeardViewPool() {
if (mRecyclerPool == null) {
mRecyclerPool = new RecycledViewPappreciateool();
}
return mRecyclerPool;
}
}
// 缓存池
public static class RecycledViewPool {
// 单类型缓存列表
static class ScrapData {
final ArrayList<ViewHolder&缓存的视频怎样保存到本地gt; mScrapHeap = new ArrayList<>();
}
// 多类型缓appstore存列表构成的缓存池(以 int 为键)
SparseArray<ScrapDat缓存兼并东西m3u8a> mScrap = new SparseArrappearay<>();
public void putRecycleGodView(ViewHolder scrap) {
// 获取 ViewHolder 类型
final int viewType = scrap.getItemVie公积金借款w枸杞Type();
// 获取指定类型的 ViewHolder 缓面试存列表
final Arraydenon是什么品牌List<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
...
// ViewHolder 实例存入缓存列表
scrapHeap.add(scrap);
}
}
}

RecycledViewPool用一个SparseArray将不同类型的 ViewHolder 实例缓存在内存,每种类型对应一个列表。当有相同类型的表项刺进列表时,不用从头创建 Vapp装置下载iewHolder 实例(施行 onCreateViewHoappearlder()),从缓存池中获取即可。

关于缓存池的详细解析能够点击RecyclerView 缓存机制 | 收回到哪去?

1. 表项自动移出屏幕

这种收回表项的场景是最常枸杞见的。作用图如下:

RecyclerView 面试题 | 哪些情况下表项会被收回到缓存池?

为啥要等 item 3 滚出屏背地里,item 1 才刚刚被收回,而 item 4 滚出屏背地里,item 2 立马被收回了?

这是由于mCachedViews的存在,它是默许大小为 2 的列表。用于缓存移出屏幕表项的 ViewHolder。

悉数移出的表项都denominate是什么牌子会顺次被缓存至其间,当mCachedViews满时,按照先进先出原则,将最早存denominate是什么牌子入的 ViewHolder 实例移除并转存至RecycledViewPool,即缓存视频怎样转入相册缓存池中。

所以 item 1 和 2 移出屏幕时,正好填满mCachedViews,当 item 3 移出屏幕时,item 1 就被挤出并存入缓存池。更详细的源码盯面试梢剖析能够点击RecyclerView 缓存机制 | 收回到哪去?

那 RecyclerView 在翻滚中是怎样判别哪些表项应该被收回?

在上一面试技巧和注意事项篇文章中详细剖析了列表翻滚时,表项是怎样被收回的,现appear援引定论和图示如下。

  1. RecyclerView 在翻滚产生之前,会依据预计翻滚位移大小来选择需求向列表中填充多少新的表项。缓存和下载的差异在填充表项的一同,也会收回表项,收回的依据是 limit 隐形线

  2. limit 隐形线 是 RecyclerView 在翻滚产生之前依据翻滚位移核算出来的一条线,它是选择哪些表项该被收回的重要依据。它能够了解为:隐形线当时地点方位,在翻滚完毕后会和列表顶部堆叠。

  3. limit 隐形线 的初始值 = 列表当时可见表项的底部到列表底部的距离,即列表在不填充新表项时,能够滑动的最大距离。每一个新填充表项消耗的像素值都会被追加到 limit 值之上,即limit 隐形线会跟着新表项的填充而不断地下移。

  4. 触发收回逻辑时,会遍历当时悉数表项,若某表项的denouement底部坐落limit 隐形线下方,则该表项上方的悉数表项都会被收回。

下图形象地描绘了 limit 隐形线(图中赤色虚线):
RecyclerView 面试题 | 哪些情况下表项会被收回到缓存池?

收回逻辑落实在源码公积金借款上,就是如下(0-5)的调用链:

public cla缓存ss RecyclerView {
public final class Recycler {
/appearance/ 5
p宫崎骏ublic void recycleView(View view) {...}
}
public abstract static class Ldeno是什么意思ayoutManagappleer {
public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
final View view = getChildAt(index);
removeViewAt(index);
// 4
recycler.recycleView(view);
}
}
}
public class Li龚俊nearLayoutManager {
private void recycleChildren(RecyclerView.Recyclerdenounce recycler, int star枸杞tIndex, int endInde面试毛遂自荐简略大方x) {
// 3:收回索引值为 endIndex -1 到 startIndex 的表项
for (int i = endIndex - 1; i >= startIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
}
private void recycleViewsFromSt面试毛遂自荐3分钟通用art(RecyclerView.Recycler recycler, int scroldenon是什么品牌功放机lingOffset,int noRecycleSpace) {
...
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
// 2
recycleChildrenappearance(recycler, 0, i);
}
}
}
private void recycleByLayou工商银行tState(RecyclerView.Recycler recycler, LayoutState layoutState) {
// 1
recycleViewsFromStart(recycler, scr面试ollingOffset, noRecycleSpace);
}
iapp装置下载nt fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.approachState state, boolean stopOnFocus龚俊able) {
...
// 循环填充表项
while ((layoutState.mInfinite || remainingSpace > 0)denon音响 &a作业细胞mp;& layoutS面试技巧tate.hasMore(state)) {
// 填充单个表项
layoutCdenouementhunk(recycler, state, layoutState, layoapproveutChunkResult);
...
if (la枸杞youtState.mScrollingOffset !缓存视频怎样转入相册= LayoutStat面试需求预备什么e.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChun面试毛遂自荐简略大方kResult.mConsumed;
// 0:收回表项
recycleByLayoutState(recycler, layoutState);
}
...
}
}
}

每填充一个表项都会遍历已加载的悉数表项,以检测其间是否有能够收回的。

若对定论的源码剖析进程感兴趣,能够点击RecyclerView 面试题 | 翻滚时表项是怎样被填充或收回的?

2. 表项被挤出屏幕

当列表中有表项刺进,把现有Go表项宫颈癌挤出屏幕时,也会产生表项收回。作用图如下:
RecyclerView 面试题 | 哪些情况下表项会被收回到缓存池?

这种场景下 item 2 会狗狗币被收回,当表项动画完毕后,就会触发表项收回逻辑:

// Recycl面试必问10大问题答复erView 默许表项动画器
public class Default缓存视频在手机哪里找ItemAnimator extends SimpleItemAn缓存的视频怎样保存到本地imator {
// 启动表项位移动画
void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
final ViewPropertyAnimator animation = view.animate();
anAPPimation.setDuration(getMoveDuration()).setListener(ne缓存整理w AnimatorListenerAdapter() {
@Override宫崎骏
public void onAnimationEnd(Anima面试必问10大问题答复tor animator) {
// 往上分发动画完毕作业
dispatchMoveFinished(holdedenon是什么品牌功放机r);
...
}
}).start();
}
}
public abstract class SimpleItem龚俊An面试技巧imator extendsappearance RecyclerViewdeno是什么意思.ItemAnimator {
public fdenon功放说明书inal void dispatchMoveFinished(RecyclerView.ViewHolder item) {
// 继续往上分发动画完毕面试技巧作业
dispatchAnimationF面试问题大全及答案大全i缓存视频怎样转入本地视频nished(item);
}
}
public class RecyclerView {
pu面试blic abstract static缓存兼并东西m3u8 class ItemAnimator {
private ItemAnimatorListener mListener = nudeno是什么意思ll;
public final void dispatchAnimationFinished(ViewHolderGo viewHolder) {
// 将动画完毕作业分发给监听器
if (mListener != null) { mListener.onAnimationFinished(viewHolder); }
}
}
privapprovea宫颈癌te c面试毛遂自荐一分钟lass ItemAn面试毛遂自荐简略大方imatorRestoreListener implem缓存ents ItemA缓存的视频怎样保存到本地nimator.ItemAnimatorListener {
@Ovapproacherride
public void o狗狗币nAnimationFinished(ViewHolder item) {
// 设置 ViewHolder 为可收回的
item.setIsRecyclable(true);
// 收回表项
if (!re缓存的视频怎样保存到本地moveAnimatingVi面试毛遂自荐ew(item.itemView) && item.isTmpDetached()) {
removeDetacdenotehedView(item.itemView, false);
}
}
}
bapp装置下载oolean removeAnimatingVappleidiew(View view) {
startInterceptRequestLayout();
final boolean r宫颈癌emove面试毛遂自荐d = mChildHelper.removeappleViewIfHidden(view缓存视频怎样转入本地视频);
// 当表项做完位移动画后的确移出了屏幕
if (removed) {
fin面试必问10大问题答复al Viewdenon是什么品牌功放机Holder v枸杞iewHolder = getChildViewHolderInt(view);
mRecycler.unscrapView(viewHolder);
// 收回 ViewHol公积金借款der
mRecyappstorecler.recycleViewHolderInternal(viewHolder);
}
...
return removed;
}
}缓存兼并东西m3u8

RecyclerView 的表项动画器将移动表项动画的完毕作业层层传递,究竟传递到了 RecyclerViapplicationew 内部的监听器,由监听器告诉 Recycler 触发表项收回动作。

3. 高面试毛遂自荐一分钟速缓存射中的 ViedenouncewHolder 变脏

变脏的意思是表项需求重绘,即调用onBindViewHolder()从头为表项绑定数据。

RecyclerView 中面试毛遂自荐范文通用有四级缓存,它会优先去高速缓存中找 Vi缓存整理ewHolder 实例。缓存池是其间速度最慢的,由于从中取出的 ViewHolder 需求从头施行oappreciatenBindViewHol面试der()scrapview cache的速度都比它快,但射中后需求进行额appearance外的校验(关于四级缓存的详解能够点击这儿):

public class Rec面试毛遂自荐一分钟yclerView
public final class Recycler {
// RecyclerView 获取 ViewHolder 的进口
ViewHolder tryGetViewHolderForPos缓存视频itionByDeadline(int position,面试booleapproachan枸杞 dryRun, long deadlineNs) {
// 从 scrap 或 view cache 中获取 ViewHolder 实例
holder = getdenounceScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
// 若缓存射中
if (holder != null) {
// 校验 ViewHolder
if (!vdenon是什么品牌功放机alidateViewHo缓存兼并东西m3u8lderForOffsetPosition(holder)) {
// 校验失利
if (!ddeno是什么意思ryRun) {// dryRun 一直为 false
....
// 收回射中的 V缓存兼并东西m3u8iewHolder (丢到缓存宫颈癌池)
recycleViewHolderInternal(holder);
}
// 符号从 scrap 或 view cache 中获取缓存失利
// 会触发从其他缓存继续获取 ViewHolder实例
holder = null;
} else {
// 符号校验成功
fromScrapOrHiddeGonOrCache = true;
}
}
....
}
}
}

从 scr面试毛遂自荐ap 或 view cache 射中的 ViewHolder 会从三个方面被校验:

  1. 表项是否被移除
  2. 表项 viewType 是否相同
  3. 表项 id 是否相同
public class RecyclerView{
public final class Recycler {
// 校验 ViewHolder 合法性
boolean validappreciateateViewHolderForOffsetPosition(ViewHolder holder) {
// 假设表项已被移除缓存视频
if (holder.isRemoved()) {
// 是否在 pappstorereLayout 阶段
return mState.isPreLayout();面试技巧
}
ifdenon是什么品牌功放机 (!mState.isPreLayout()) {
// 检查从缓存中获取的 ViewHolder 是否和 Adapter 对应方位的 ViewHolder 有相同的 viewType
final i面试需求预备什么nt ty面试问题大全及答案大全pe = mAdapter.getItemViewType(holder.mPositi缓存和下载的差异on);
if (type != holder.getItemViewType()) {
return false;
}
}
// 检查从缓存中获取的 ViewHolder 是否和 Adapter 对应方denominate是什么牌子位的 ViewHolder 有相同的 id
if (mA面试需求预备什么dapter.hgoogleasStableIds()) {
return holde面试毛遂自荐范文通用r.getItemId() == mAdapter.getItemId(holder.mPosition);
}
return true;
}
}
}

只要和指定方位表项具有相同的 viewTyp工商银行e 或相同的 id 时,scra枸杞pview cache中射中的缓存才会被运用。不然即便射中也会视为无效ViewHolder被丢到缓存池中。

4. mCachedViews 中缓存的表项被删去

表项移出屏背地里,马上被收回到mCachedViews结构中。若恰巧该表项又被删去了,则表项对应的 ViewHolder 从mCachedViews结构中移除,并增加到缓存池中:

public class RecyclerView {
public final class Recycler {
void recycleCachedViewAt(int cachedViewIndex) {
// 从 mCacheViews 结公积金构中获取面试毛遂自荐范文通用指定方位的 ViewHold龚俊er 实例
ViewHolder viewHolder = mCachedView面试毛遂自荐简略大方s.get(cachedVi面试毛遂自荐ewIndex);
// 将 ViewHol缓存der 存入缓存池
addViewHolderToRecycledViewPdenon音响ool(viewHolder, true);
// 将 ViewHolder 从 mCaapplicationcheViews 中移除
mCachedViews.remove(cachedViewIndex);
}
void addViewHolderToRecycledViewPool(@缓存视频兼并软件NonNull ViewHolder holder, boolean dispatchRecycled) {
..缓存视频怎样转入相册.
getRedenon是什么品牌cycledViewPool().putdenominateRecy缓存兼并东西m3u8cledView(holder);
}
}
}

5. pre-layout 中额定填充的表项在 post-layout 中被移除

pre-layout & post-layo面试问题大全及答案大全ut

pre-layAPPoutpost-layout在Recycler面试View 动画原理 | pre-layout,post-layout 与 scrap 缓存的联络有介绍过denominate,援引如下:

RecyclerView 要做表项动画,

为了承认动画的种类和起终点,需求比对动画前和动画后的两张“表项快照”

为了获得两张快照,就得布局两次,分别是 pre-layout 和 post-layout(布局就是往列表中填充表项),

为了让两次布局互不影响,就不得不在每次布局前先铲除上一次布局的内容(就比如先铲除面试需求预备什么画布,从头作画),

但是两次布局中所需的某些表项大概率是一摸相同的,若在铲除画布时,把表项的全appearance部信息都一同denote铲除,那从头作画时就会花费更多时间(面试需求预备什么从头创建 Vdenominate是什么牌子iewHolder 并绑定数据),

RecyclerView 采取了用空间换时间的做法:在缓存兼并东西m3u8铲除画布时把表项缓存在 scrap 缓存中,以便在填充表项能够射中缓存,以缩短填充表项耗时。

RecyclerView 面试题 | 哪些情况下表项会被收回到缓存池?

Gidenouementf 的场景中,在 pre-layout 阶段,item 1、itemapproach 2、item 3 被填充到列表中,构成一张动画前的表项快照。而 post-layout 将 item 1、item 3 填充到列表中,构成一张动画后的表项快照。

对比这两张快照中的 item 3 的缓存方位就能知道它该从哪里平移到哪里,也知道 item 2Go 需求做消失动画,当动画完毕后,itdeno是什么意思em 2 的 ViewHolder 会被收回到缓存池,宫崎骏收回的调用链和“表项被挤出屏幕”是相同的,都是由动画完毕来触发的。

在 pre-layout 阶段填充额定表项

考虑其他一种场景,这次不是移除 item 2,而是denon音响更新它,比如把 item 2 更新成 item 2.1,那 pre-layoutdenominate宫崎骏会将 item 3 填充进列表吗?

RecyclerView 动画原理 | 换个姿态看源码(pre-layout) 详细剖析了,在 pre-layAPPout 阶段,额定的表项是怎样被填充到列表,其间要害源码再拿出来看一下:

public class LinearLayoutManager{
// 向列表中填充表项
intapp装置下载 fill(RecyclerView.Recycler recycler, LayoutState layoutStappleate, Recy面试毛遂自荐clerView.State state, boolean stopOnFocusable) {
...
// 核算剩下空间
int remainingSpace = layoutState.mAvailable + layoutStaappreciatete.mExtraFillSpace;
// 循环填充approach表项,直到没有剩下空间
while ((layoutState.mInfinite || re缓存整理mainingSpace > 0) && layoutState.hasMore(stat面试毛遂自荐一分钟e)) {
layoutChunkResult.resetInternal();
// 填充单个表项
layoutChunk(recycler, state, layo面试问题大全及答案大全utState, layoutChunkResult);
...面试
// 在列表剩下空间中扣除刚填充表项所消耗的空间
if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null || !state.denoteisPre面试需求预备什么Layout()) {
layoutState.mappstoreAvailable -appear= layoutChunkResult.mConsumed;
remainingSp面试毛遂自荐3分钟通用ace -= la缓存视频兼并软件youtChunkResultdenote.mConsumed;
}
...
}
...
}
}

直觉上,每填充一个表项都应该将其消耗的空间扣除,但扣除宫颈癌逻辑套在了一个 if 中,即扣除是有条件的。

条件表达式中一共有三个条件,在预布局阶段apple!state.isPreLayout()必定是 false,layoutState.缓存视频兼并mScrapList != null也是 false(断点告诉我的),终究一个条件!layoutChunkResult.mIgnoreConsumed缓存了选择性的作用,它在填充单个表项时被赋值:

public class Lindeno是什么意思earLayoutManager {
void layo公积金借款utChunk(RecyclerVideno是什么意思ew.Recycler rec面试技巧和注意事项ycler, RecyclerView.State state,LayoutState layoutState, LayappleoutChunkResult result) {
// 获取下一个该被填充的表项视图
View view = layoutState.next(recycler);
...// 省掉了施行填充的详细逻辑
// 假设表项被移除或被更新 则 mIgnoreConsumed 置为 true
if (params.is缓存整理ItemRemoved() || pAPParams.isItemChanged()) {
result.mIgnoreConsumed = true;
}
...
}
}

lay缓存out面试毛遂自荐范文通用ChunkResult被作为参数传入layoutChunk(),而且当填充表项是被删去的或是被更新作业细胞的,就将layoutChunkResdenominate是什么牌子ult.mIgnoreConsappleidumed置为 true。标明该表项尽管被填充进了列表但是它占用的空间应该呗忽略。至此能够得出定论:

在预布局阶段,循环填充表项时,若遇到被移denominate是什么牌子除的或是被更新的表项,则会忽略它占用的空appearance间,剩下空间被用来加载额denominate外的表项,这些表项在屏幕之外,本来不会被加载。

尽管这狗狗币定论就是代码的本意,但仍是有一点让我不太了解。忽略被移除表项占用google的空间简略了解,那为啥更新的表项也一同被忽略?

那是由于,更新表项时,表项的布局或缓存许产生改变(取决于onBindViewHolder()的完成),如果表项布局变长,则会构成其他表项被挤出屏幕,或是表项变短,构成新表项移入approve屏幕。

记载表项动画信息

在RecyclerView 动画原理appleid | 怎样存储并使用动画特征值?中介绍了 RecyclerView 是怎样存储动画特征值的,现征枸杞引如下:

  1. RecyclerView 将表项动画数据封装了两层,顺次是ItemHolderInfoInfoRecord,它们记缓存视频兼并软件载了列表预布局和后布局表项的方位信息,即表项矩形区域与列表左上角的相对方位,它还用一个int类型的标志位来记载表项履历了哪些布局阶段,以判别表项应该做的动画类型(呈现,deno是什么意思消失,坚持)。

  2. InfoRecord被会合存放在一个商铺类ViewInfoStore中。悉数参加动画的表项的Viewdenon是什么品牌功放机HolderInfoRecord都会以键值对的方法存储其间。

  3. RecyclerViappleew 在布局的第三阶段会遍历商铺工商银行类中悉数deno的键值对面试需求预备什么,以InfoRecord中的标志位为依据,判别施行哪种工商银行动画。表项预布局和后布局的方位信息会一同传递给RecyclerView.ItemAndenon是什么品牌imatgoogleor,以触发动画。

在 pre-layout 阶段,存储动画信息的代码如下:

public class Recdenon功放说明书yclerView {
private void dispatchLayoutStGoep1() {
...
// 遍历列表中现有表项缓存视频兼并
int count = mChildHelper.getChildCount();
for (int i = 0; i < count;缓存视频怎样转入相册 ++i) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
// 为表项构建 ItemHolderInfo 实例
final ItemHolderInfo an公积金借款imationInfo = mItemAnimator.recordPreLa缓存youtInformation(mappstoreState, hold面试毛遂自荐er, ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),holder.getUnmodifiedPayloads());
// 将 ItemHolderInfo 实例存入 ViewInfoStore
mViewInf缓存视频怎样转入相册oStore.addToPreLayout(holder, animationInfo);
}
...
// 预appearanceGo
mLayout.onLayouapproachtChildren(mRecycler, m工商银行State);
// 预布局后,再次遍历悉数孩子(预布局或许填充额定的表项)
for (int i = 0; i < mChildHelper.getChildAPPCount();面试 ++i) {
finaldenon音响 View child = mChildHelper.getChildAt(i);
final ViewHolder viewHolder = getChildViewHolderInt(child);
// 过滤掉带有 FLAG_PRE 标志位的表项
if (!mViewInfoStore.isInPreLayout(viewHolder)) {
// 为额定填充的表项构面试毛遂自荐简略大方建 ItemHolderInfo 实例
final ItemHolderInfoapplication animationInfo = mItemAnimator.recordPreLayoutInformation(mState, viewHold作业细胞er, fappstorelags, viewHolder.getUnmodifiedPayloads());
/面试毛遂自荐一分钟/ 将 ItemHolderInfo 实例存入 ViewInfoStore
mViewInfoStore.addToAppearedInPreLaydenominate是什么牌子outHolders(v缓存视频iewHolder, a面试技巧nimationInfo);
}
}
...
}面试毛遂自荐
}
class ViewInfoStore {
void addTo狗狗币PreLayout(Recycle工商银行rView.ViewHolder holddenominate是什么牌子er, RecyclerViedeno是什么意思w.ItemAnimator.ItemHolderInfo info) {
InfoRecord record = mLayoutHolderMap.g缓存视频在手机哪里找et(holder);
if (record == nul龚俊l) {
record = InfoRecord.obtain();
mLayoutHdenounceolderMap.pdenounceut(holder, record);
}
record.preInfo = info;
// 增加 FLAG_PRE 标志位
record.flags枸杞 |= FLAG_PRE;
}
void adappearancedToAppearedInPreLayoutHolders(RecyclerView.ViewHolde面试技巧和注意事项r hold作业细胞er, RecyclerView.ItemAnimator.ItemHolderInfo info) {
InfoRecord record = mLayoutHolderMap.get(holder);
if (denoterecord == null) {
record = InfoRecord.obtain();
mLayoutHolderMap.put(hdenominateolder, record);
}
// 增加 FLAG_APPEAR 标志位
record.flags |= FLAG_APPEAR;
recorappled.preInfo = info;
}
}

在 pre-lGoayout 的前后,遍历了两次表项。

关于 Demo 的场景来说,第一次遍历,item 1 和 2 的动画特征被存入 ViewInfoStore 并增加了FLdenon音响AG_宫颈癌PRE标志位。遍历完毕后施行预布局,把屏幕之外的 item 3 也填充到列表中。再紧接着的第2次遍历中,item 3 的动画特征也会被存入 ViewIdenon音响nfoStore 并增加了FLAG_APPEAR标志位,标明该表项是在预布局进程中额定被填充的。

在 post-layout 阶段,为了构成动画后的表项快照,得清空列表,从头填充表项,出于时间功用的考虑,被移除表项的 ViewHolder 缓存到了 scrap 结构中(item 1 2 3的 ViewHodler 实例)。

从头向列表中填充 item 1 和更新后的 item 2,它们的 ViewHolder 实例能够从 scrap面试毛遂自荐一分钟 结构中快速获取,不用再施行 onCreateViewHolder()。填充完后,列表的空间现已用完,而 scrap 结构中还剩一个 item 3 的 Vieapp装置下载wHolder 实例。它会在 post-layoudeno是什么意思t缓存视频在手机哪里找 阶段被增加新的标denounce志位:

public class LinearLayoutManager {
// 在 dispat面试必问10大问题答复chLayoutStep2() 中第2次调用 onLayoutChildren(公积金) 进行 post作业细胞-layout
public void onLayoutChildren(RecyclerView.Recycler re缓存视频怎样转入本地视频cycler, RecyclerView.State state)appleid {
...
// 为动画而进行布局
layoutForPr公积金edictive面试技巧Animationsapp装置下载(recycler, state, startOffset, endOffset);
}
private void layoutForPredictiveAnimations(RGoecycler面试毛遂自荐View.Recycler recycler,RecyclerView.State state, int startOffsapplicationet,int endOffset) {
final List<RecyclerView.ViewHolder> scrapList = recycler.getS缓存兼并东西m3u8crapList();
final int scrapSize = sAPPcrapList.size();
/面试技巧/ 遍历 scraappreciatep 结构
for (int i = 0; i < scrapSize; i++) {
RecyclerView.Vie面试wHolder scrap = scrapList.ge缓存t(i);
final int positi缓存视频在手机哪里找on = scrap.getLayoutPosition();
final int direction = position < firstChi缓存视频兼并软件ldPos != m缓存的视频怎样保存到本地ShouldReverseLayout?apple LayouappearancetState.LAY枸杞OUT_START : LayoutSt面试毛遂自荐范文通用ate.LAYOUT_END;
// 核算 scrap 结构中对应表项所占用的空间
if (diappearancerection == LayoutState.LAYOUT_STARappreciateT) {
scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
} else {
scrapExtraEnd += mOrientatidenon是什么品牌功放机onHelper.getDecoratedMeasurement(scrap.itemView);
}
}
// mLayoutState.mScrapList 被赋值
mLayoutState.mScrapList = scrapLi枸杞st;
// 再次检验填充表项
if (scrapExtraStart > 0)app装置下载 {
...
fill(denon是什么品牌功放机recycler, mLayoutSta缓存的视频怎样保存到本地te, state, false);
}
if (scrapExtraEnd > 0) {
...
fill(rec面试毛遂自荐ycler, mLayoutState, state, false);
}
mLayoutState.mScrapList = null;
}
void layoutChunk(RecyclerView.Recycler recycler, RecycleapplerView.State state, LayoutState layoutState, LayoutChunkResul面试毛遂自荐范文通用t result) {
View view = layoutState.next(recycler);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParapproveams();
// 分支1:把表项填充到列表中
if (layoutState.mScrapList =缓存和下载的差异= null) {
if (mShoudeno是什么意思lapproachdReverseLayout == (layouapprovetState.mLayoutDirection == LayoutState.LAYOUT_START)) {
addView(view);
} else {
add狗狗币Vie宫颈癌w(view, 0);
}
}
// 分支2:把表项动画信息存储到 ViewInfoStore 中
else {
if (mShouldReverseLayo公积金借款ut == (lay缓存视频outState.mLay缓存视频兼并outDirection == LayoutState.LAYOUT_START)) {
// 托付给父类 Laydenon功放说明书outManger
addDis龚俊appearin缓存gView(view);
} else {
addDisappearingView(view, 0);
}
}
...
}
}

这次填充表项的layoutChunk()由于applicationlayoutState.mScrapList不为空,会走不相同的面试毛遂自荐分支,即调用addDisapdenon功放说明书pearingView()

public clasapplications RecyclerView {
public abstract static class LayoutManager {
public void a缓存和下载的差异ddDisappearingView(View缓存视频兼并软件 child) {
addDisappearingV宫崎骏iappearew(child, -1);
}
public void addDisappearingView(View child, int index) {
addViewInt(child, i面试需求预备什么ndex, true);
}
private void addViewInt(View child, int index, booldenon功放说明书ean disappearing) {
fiGonal ViewHolderapplication holder = getChild狗狗币ViewHolderInt(c面试毛遂自荐hild);
if (disappearing || holder.isRemoved()) {
// 置 FLAG_DISAPPEARED 标志位
mRecyclerView.mViewInfoStore.addToDisappearedInLayout缓存视频兼并软件(holder);
} else公积金借款 {
mRecyclerView.mViewInfoStore.removeFromDisappear公积金edInLayout(holder);
}
...
}
}
}
class ViewInfoStoredeno是什么意思 {
// 置 FLAG_DISAPPEARED 标志位面试毛遂自荐范文通用
void addToDisappearedInLayout(RecyclerView.ViewHolder holder) {
InfoRecord record = mLayoutHolderMap.get(holder);denon是什么品牌功放机
if (recor公积金借款d == null) {
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.flags |= FLAG_DISAPPEARED;
}
」

至此 item 3 在履历了 pre-layou面试毛遂自荐3分钟通用t 和 post-layout 后,它的动画信息被存储在ViewInfoappleStore中,且增加了两个标志位,分别是FLAG_APPEARFLAG_DISAPPEARED

在布局的第三appearance阶段,会调用ViewInfoStore.procGoess()触发动画:

public class Recycler缓存View {
private void dispatchLayoutStep3(denote) {
...
// 触发表项施行动画面试毛遂自荐
mViewInfoStore.process(mViewInfoProcessCallback);
...
}
}
class ViewInfoStore {
void process(ProcessCallback callback)缓存视频 {
// 遍历悉数参加动画表项的方位信息
for (int index = mLayoutHolderMap.size() - 1; index >= 0; iAPPndex--) {
// 获取表项appleid ViewHolder
final RecyclerView.ViewHolder viewHolder = mLayoutHolderMap.keyAt(i宫崎骏ndex);
// 获取与 ViewHolder 对应的动画信息
fin面试al InfoRecord record = mLayoutHolderMap.removeAt(index);
// 依据动画信息的标志位承认动画类型以施行对应的 ProcessCallback 回调
if ((record.googleflags & FLAG_APPEAR_ANappearD_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
...
}
}
}
}

Demo 中的 item 3 会射中第一个 if 条件,由于:

class ViewInfoStore {
static class InfoRecord {
// 在 post-layout 中消失
static final int FLAGappreciate_DISAPPEARED = 1;
// 在 pre-layout 中呈现
static final i工商银行nt FLAG_APPEAR = 1 << 1;
// 上两作业细胞者的合体
stati缓存视频兼并c final int FLAGapp装置下载_APPEAR_AND_DISAPPEAR = FLAG_APPEAR |公积金借款 FLAG_DISAPPEA枸杞RED;
}
}

收回 item 3 到缓存池的逻面试毛遂自荐辑就在callback.unused(vi面试必问10大问题答复ewHolder)中:

public class RecapproveyclerView {
privat面试必问10大问题答复e final ViewInfoStore.ProcessCallback mVie面试必问10大问题答复wInfoProcessCallback = new ViewInfoStore.ProcessCallbdeno是什么意思ack()面试毛遂自荐一分钟 {
...
@Override
public void unused(ViewHolder viewHolder) {
// 回APP收没有用的表项
mLayout.removeAndRecycleView(viewHolder.itemViewdenon音响, mRecyclAPPer);
}
};
public abstract static class LayoutManagappreciateer {
public void removeAndRecycleView(@NonNull View c面试hild, @NonNull Recycler recycleappreciater) {
removeView(child);
// 托付给 Recycler
recycler.recycleView(child);
}
}
public final class Recycler {
p面试毛遂自荐ublic void recycleView(@NonNull View view) {
// 收回表项到缓存池
recycleViewHolderInternal()
}
}
}

至此能够得出定论:

悉数在 pre-layout 阶段被额定填充的表项,若究竟没能在 post-layout 阶段也填充到列表中,就都会被回到到缓存池。

举荐阅览

  1. RecyclerView 缓存机制 | 怎样复用表项?

  2. RecyclerView 缓存机制 | 收回些公积金什么?

  3. RecyclerView 缓APP存机制 | 收回到哪去?

  4. RecyclerView缓存机制 | scrap view 的生命周期

  5. 读源码长知缓存和下载的差异识 | 更好的RecyclerVGoiew点击监听器

  6. 署理方法使用 | 每当为 Recycle缓存兼并东西m3u8rView 新增类龚俊型时就很抓狂

  7. 更好的 RecyclerView 表项子控件点击监听器

  8. 更高效地改写 RecyclerView | DiffUtil二次封装

  9. 换一个思路,超简略的RecyGoclerView预加载

  10. RecyclerView 动画原理 | 换个姿态看源码(pre-layout)

  11. RecyclerViewdenounce 动画原理approach | pre-layout,post-layout 与 scrap 缓存的联络

  12. Recyc缓存整理lerView 动画原理 | 怎样存储并使用动画特征值?