【进阶】RecyclerView源码解析(三)——深度解析缓存机制

RecyclerView源码浅析 大致了解RecyclerView的绘制流程。后期扩充RecyclerVIew的复用机制、动画、滚动处理。 阅读详情

1.【进阶】RecyclerView源码解析(一)——绘制流程
2.【进阶】RecyclerView源码解析(二)——缓存机制
3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制

上一篇博客从源码角度分析了RecyclerView读取缓存的步骤,让我们对于RecyclerView的缓存有了一个初步的理解,但对于RecyclerView的缓存的原理还是不能理解。本篇博客将从实际项目角度来理解RecyclerView的缓存原理。
项目的截图如下:Demo

其中可以看到,这里是一个我们经常使用RecycleView实现列表。右侧输出面板展示了ScrapView的最大数量,CacheView的数量和内容,Pool中存在的内容。左侧面板展示了onBindViewHolder和onCreateViewHolder的过程。(Demo是基于一篇博客的Demo的拓展:手摸手第二弹,可视化 RecyclerView 缓存机制)
Demo地址:RecyclerViewStudy感兴趣的可以顺手点个star~

1.ScrapViews

起初,我对于这个缓存的概念一直很模糊,我尝试过很多方法想要将这个缓存中的View读取出来看看里面的内容,但是发现这个缓存的大小总是为0,这个就让我很疑惑一个大
小总是为0的缓存还有什么作用?
无意中读到了一篇博客,这篇博客对于RecyclerView提出了Detach和Remove的概念的区别,对于RecycleView的ScrapView进行了讲解。

1.1 Detach和Remove

所以我们需要区分两个概念,Detach和Remove

detach: 在ViewGroup中的实现很简单,只是将ChildView从ParentView的ChildView数组中移除,ChildView的mParent设置为null, 可以理解为轻量级的临时remove, 因
为View此时和View树还是藕断丝连, 这个函数被经常用来改变ChildView在ChildView数组中的次序。View被detach一般是临时的,在后面会被重新attach。
remove: 真正的移除,不光被从ChildView数组中除名,其他和View树各项联系也会被彻底斩断(不考虑Animation/LayoutTransition这种特殊情况), 比如焦点被清除,从TouchTarget中被移除等。

1.2 缓存作用

首先我们要了解,任何一个ViewGroup都会经历两次onLayout的过程,对应的childView就会经历detach和attach的过程,而在这个过程中,ScrapViews就起了缓存的作用,这样就不需要重复创建childView和bind。
所以ScrapView主要用于对于屏幕内的ChildView的缓存,缓存中的ViewHolder不需要重新Bind,缓存时机是在onLayout的过程中,并且用完即清空

1.3 Demo验证

我们可以看一下demo验证一下我们的想法。
首先我们重写了RecylclerView的onLayout方法。

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        onLayoutListener.beforeLayout();
        super.onLayout(changed, l, t, r, b);
        onLayoutListener.afterLayout();
    }

在beforLayout时设置通过反射将RecyclerView内部的mAttachedScrap替换成我们自己重写的数据结构。

public void setAllCache() {
        try {
            Field mRecycler =
                    Class.forName("android.support.v7.widget.RecyclerView").getDeclaredField("mRecycler");
            mRecycler.setAccessible(true);
            RecyclerView.Recycler recyclerInstance =
                    (RecyclerView.Recycler) mRecycler.get(this);

            Class<?> recyclerClass = Class.forName(mRecycler.getType().getName());
            Field mAttachedScrap = recyclerClass.getDeclaredField("mAttachedScrap");
            mAttachedScrap.setAccessible(true);
            mAttachedScrap.set(recyclerInstance, mAttachedRecord);
            Field mCacheViews = recyclerClass.getDeclaredField("mCachedViews");
            mCacheViews.setAccessible(true);
            mCacheViews.set(recyclerInstance, mCachedRecord);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

为什么要这样做哪?这里利用了Hook的思想。这样的话,RecyclerView内部在对mAttachedScrap进行操作的时候,比如RecyclerView内部对于mAttachedScrap的添加是使用add(T t)这个方法,这样我们设置的子类只要重写这个add(T t)的方法,在添加的时候就会调用我们子类重写的add方法。

    @Override
    public boolean add(T t) {
        RecyclerView.ViewHolder vh = (RecyclerView.ViewHolder) t;
        RcyLog.log(key + "添加---【position=" + vh.getAdapterPosition() + "】");
        if (canReset) {
            if (size() + 1 > lastSize) {
                maxSize = size() + 1;
            }
        }
        return super.add(t);
    }

    @Override
    public T remove(int index) {
        RecyclerView.ViewHolder vh = (RecyclerView.ViewHolder) get(index);
        RcyLog.log(key + "移除---【position=" + vh.getAdapterPosition() + "】");
        return super.remove(index);
    }

可以看到这里,当RecyclerView内部对mAttachedScrap进行add和remove的时候,我们都会进行打印log。并且记录一下maxSize。按照我们的猜想,RecyclerView会在onLayout的过程中对mAttachedScrap进行添加和移除操作,执行完后,mAttachedScrap的大小为0。
第一次进入应用
Log截图
可以看到我们打开应用Demo的这个操作,没有做其他任何操作,仅仅是打开,mAttachedScrap经历了添加屏幕内9个ChildView的过程,并将9个ChildView移除的过程。而mAttachedScrap的大小刚好为屏幕内可以显示的Item的数量。
为什么说不需要重写Bind哪?通过上篇博客,我们从源码角度对RecyclerView的缓存有了一个初步的了解:

//先从scrap中寻找
        for (int i = 0; i < scrapCount; i++) {
            final ViewHolder holder = mAttachedScrap.get(i);
            if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                    && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
                holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                return holder;
            }
        }


         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()) {
            //如果FLAG是ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID,则需要调bind
            if (DEBUG && holder.isRemoved()) {
                throw new IllegalStateException("Removed holder should be bound and it should"
                        + " come here only in pre-layout. Holder: " + holder
                        + exceptionLabel());
            }
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
        }

可以看到,我们在Scrap中寻找的时候,是有一个判断!holder.isInvalid(),而对于需要bind的时候判断是否需要bind有一个判断holder.isInvalid()。所以两个条件是互斥的。

2.CacheViews

CacheViews其实就是和我们平常使用过程中息息相关的一个缓存。CacheViews缓存的特点是CacheViews内的缓存在复用的时候不需要调用bind,也就是在滑动的过程中,免去了bind的过程,提高滑动的效率。
#### 2.1 缓存源码
首先来看一下对于CacheViews内缓存的获取的源码:

/ /Search in our first-level recycled view cache.
final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
// invalid view holders may be in cache if adapter has stable ids as they can be
// retrieved via getScrapOrCachedViewForId
if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
if (!dryRun) {
mCachedViews.remove(i);
}
if (DEBUG) {
Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
+ ") found match in cache: " + holder);
}
return holder;
}
}

首先我们通过源码可以知道CacheViews是一个ArrayList,可以看到获取的时候是遍历CacheViews,当缓存的ViewHolder和所需要的position相同的并且有效才可以复用。
和上面分析的一样,可以知道这个缓存的ViewHolder是有效的才可以复用,所以在判断是否需要bind的时候,就不需要重新bind了。
接着来看一下缓存的源码:
既然是缓存,那肯定是滑动过程中的比较直观:
“`
@Override
public boolean onTouchEvent(MotionEvent e) {
case MotionEvent.ACTION_MOVE: {
………
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
vtev)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
……..
return true;
}

boolean scrollByInternal(int x, int y, MotionEvent ev) {
    ......
        if (x != 0) {
            consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
            unconsumedX = x - consumedX;
        }
        if (y != 0) {
            consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
            unconsumedY = y - consumedY;
        }
       .......
    return consumedX != 0 || consumedY != 0;
}


可以看到这里省略了部分代码,在
onTouchEvent的ACTION_MOVE事件中,可以看到,这里对canScrollVertically方法进行了判断,并最终将偏移量传给了scrollByInternal方法,而在scrollByInternal方法中,调用了LayoutManager的scrollVerticallyBy方法。而scrollVerticallyBy最后调用了scrollBy方法。

int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
……
//调用了fill方法
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
……
return scrolled;
}

可以看到fill方法又调回了前一篇博客分析的**fill()**方法,这样就很明显了。而缓存的源码其实上面博客上面提到过一个方法
onLayoutChild()方法里面有个detachAndScrapAttachedViews“`方法。

public void detachAndScrapAttachedViews(Recycler recycler) {
        final int childCount = getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            final View v = getChildAt(i);
            scrapOrRecycleView(recycler, i, v);
        }
    }

    /**
     * 1.Recycle操作对应的是removeView, View被remove后调用Recycler的recycleViewHolderInternal回收其ViewHolder
     2.Scrap操作对应的是detachView,View被detach后调用Reccyler的scrapView暂存其ViewHolder
     * @param recycler
     * @param index
     * @param view
     */
    private void scrapOrRecycleView(Recycler recycler, int index, View view) {
        final ViewHolder viewHolder = getChildViewHolderInt(view);
        if (viewHolder.shouldIgnore()) {
            if (DEBUG) {
                Log.d(TAG, "ignoring view " + viewHolder);
            }
            return;
        }
        if (viewHolder.isInvalid() && !viewHolder.isRemoved()
                && !mRecyclerView.mAdapter.hasStableIds()) {
            //注意这里是remove
            removeViewAt(index);
            //往cacheview和pool中
            recycler.recycleViewHolderInternal(viewHolder);
        } else {
            //注意这里是detach
            detachViewAt(index);
            //存到scrap中
            recycler.scrapView(view);
            mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
        }
    }

这里就可以看到前面所说的Remove和Detach的区别,如果是remove,会执行recycleViewHolderInternal(viewHolder);方法,而这个方法最终会将ViewHolder加入CacheView和Pool中,而当是Detach,会将View加入到ScrapViews中,注意View和ViewHolder的区别,前面提到过,ScrapViews是对View的复用,而CacheView和Pool是对ViewHolder的复用。
既然是看CacheViews,那么就看一下recycleViewHolderInternal方法。

void recycleViewHolderInternal(ViewHolder holder) {
        ......
        if (forceRecycle || holder.isRecyclable()) {
            if (mViewCacheMax > 0
                    && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                    | ViewHolder.FLAG_REMOVED
                    | ViewHolder.FLAG_UPDATE
                    | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
                // Retire oldest cached view
                int cachedViewSize = mCachedViews.size();
                //如果超过默认大小,则删除第一个
                if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                //从CacheViews中删除第一个,并加入到Pool中
                    recycleCachedViewAt(0);
                    cachedViewSize--;
                }
        ......
                //加入缓存
                mCachedViews.add(targetCacheIndex, holder);
                cached = true;
            }
            if (!cached) {
                //不然直接加入Pool中
                addViewHolderToRecycledViewPool(holder, true);
                recycled = true;
            }
        .......
    }

可以看到几个关键逻辑:

1.如果超过默认大小,则会移除CacheViews中的第一个,并加入到Pool中,然后在将需要加入缓存的ViweHolder加入到CacheView中。
2.如果不能加入到CacheViews中,则加入到Pool中。

2.2 Demo验证

(1)进入应用
我们首先进入应用会发现当前CacheViews的大小是0,也就是说进入应用时没有滑动,是没有任何ViewHolder回收的,这不需要解释吧。。。,而且Bind也只走了页面渲染的0-8。
进入应用
(2)向下滑动一个,第一个移除
这时我们向下滑动,加载出第9个
滑动一个
可以看到这时候除了加载了页面的position=9,还提前加载出了position=10,执行了onBind,而这时,由于第一个移出界面,所以position=0也就被加入到了CacheViews中。
(3)向上滑动,再显示第一个
回到顶部
这时候我们会发现几个特别的点:

1.onBind的面板没有新的Log,说明新出来的position=0没有走onBind方法。
2.CacheViews中由刚才保存的position=0和position=10,变成了position=10和position=9
由此可见:
CacheViews中缓存的ViewHolder当被复用的时候是不会走Bind流程的

RecyclerPool

其实根据前一节的讲解,我们已经对RecycleView的缓存有了一个很具体的了解了,RecyclerPool其实是RecyclerView区分ListView的一个亮点。利用这级缓存我们可以实现多个RecyclerView之间的ViewHolder的复用。(关于这一点的利用我准备在下一篇博客对RecycleView使用的技巧进行举例讲解)

3.1 缓存源码

首先我们看一下ReyclerPool的结构。

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<>();
    }

可以看到RecyclerPool内部其实是一个SparseArray,可想而知,key就是我们的ViewType,而Value是ArrayList。
我们来看一下RecyclerPool的put方法。

public void putRecycledView(ViewHolder scrap) {
        final int viewType = scrap.getItemViewType();
        final ArrayList<ViewHolder> 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");
        }
        //重置ViewHolder
        scrap.resetInternal();
        scrapHeap.add(scrap);
    }

其中resetInternal方法值得我们注意。

void resetInternal() {
        mFlags = 0;
        mPosition = NO_POSITION;
        mOldPosition = NO_POSITION;
        mItemId = NO_ID;
        mPreLayoutPosition = NO_POSITION;
        mIsRecyclableCount = 0;
        mShadowedHolder = null;
        mShadowingHolder = null;
        clearPayload();
        mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
        mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
        clearNestedRecyclerViewIfNotNested(this);
    }

可以看到所有被put进入RecyclerPool中的ViewHolder都会被重置,这也就意味着RecyclerPool中的ViewHolder再被复用的时候是需要重新Bind的。这一点就可以区分和CacheViews中缓存的区别。

总结

还是那篇Bugly博客中的图片吧(都怪我太懒了。。。)
缓存总结
看过上面的分析,这张图片就很好理解了。

最后

给大家分享几篇我认为不错的RecyclerView源码分析的博客吧,我的分析其中有些地方就是从这些博客中学习来的。

1.Bugly分析ListView和RecyclerView的区别的,建议深入了解后再看
2.优快云的一个大神的分析,分了有6篇博客,值得一读
3.一篇很好的RecyclerView的源码分析博客,适合深入阅读
4.可视化RecyclerView缓存机制,也就是本篇博客Demo的参考
5.一篇将RecyclerView的缓存讲的通俗易懂的博客,源码不是比较深入,但是很好理解
。。。还有一些就不上了,以上5篇是我认为很值得反复阅读学习的。

下篇博客可能是RecyclerView分析系列的结尾篇了,可能从实际使用角度分析一些我所了解的RecyclerView的一些进阶知识

RecyclerView源码解析 基础 RecyclerView相对于以前的ListView来说,更加灵活。其所拆分出来的各个类的分工更加明确,很好地体现了我们经常所说的职责单一原则。我们这里先对其中使用到的类进行一下讲解 LayoutManager:RecyclerView的布局管理者,主要负责对于RecyclerView子View的测量和布局工作。 RecyclerView.Recycler:缓存的核心类。RecyclerView强大的缓存能力都是基于这个类来实现的。是缓存的核心工具类。 Adapter:Adapter的基类。负责将V 阅读详情

相关推荐

LLM - windows下的Dify离线部署:从镜像打包到无网环境部署(亲测,包含插件部署)

LLM - windows下的Dify离线部署:从镜像打包到无网环境部署(亲测,包含插件部署)

ocean00008899的博客 1289

RecyclerView源码讲解系列(一)总览

​从今天开始RecyclerView源码讲解系列正式开始更新,每周一篇。欢迎大家持续关注。RecyclerView是Android中十分重要的视图组件,可以说基本每个APP的主页面都用到了RecyclerView,由此可见其重要性。那么问题来了,如何对RecyclerView做性能优化、并且封装成一个简单易用且功能强大的RecyclerView。那么首先第一步就是先了解RecyclerView,对它的整体设计有一个总览的概念。

Y810614的博客 1620

LabVIEW波形图表时间轴设置:三种方法实现横坐标显示日期时间

在数据采集与设备监控场景中,波形图表的横坐标默认显示采样点序号而非日期时间,这给工程师分析历史数据带来不便。理解时间标识(Timestamp)的本质——一个自1904年起算的双精度秒数,是解决问题的关键。时间轴设置既是显示格式问题,更是数据层与显示层的协同设计。通过属性节点动态生成刻度标签、将时间标识数组直接作为X轴数据、或使用波形数据(Waveform)类型绑定t0与dt,分别适用于固定点数显示、非等间隔事件记录和等间隔连续采样等不同需求。合理选择方案并注意缓冲区预分配与时间连续性处理,可让波形图表准确呈

weixin_33416697的博客 255

RecyclerView源码解析(三):RecyclerView的缓存机制

终于分析完了这个巨长无比的方法了,现在估计还是很疑惑,现在让我们对上面这个过程进行一下总结。最后我光看到了将viewHolder添加进入RecyclerPool而没有看到清除,所以对其源码进行了查看,发现mRecyclerPool是通过ArrayList存储viewHolder,用SparseArray存储与之相关的一些信息,通过这两个容器检查了回收池中指定类型的视图是否已满。如果池已满,就直接返回,不会将视图添加到回收池中。这是为了限制池中视图的数量,以防止占用过多内存。

Tai_Monster的博客 1039

R语言 | 关联规则

关联规则(AssociationRules)是无监督的机器学习方法,用于知识发现,而非预测。 关联规则的学习器(learner)无需事先对训练数据进行打标签,因为无监督学习没有训练这个步骤。缺点是很难对关联规则学习器进行模型评估,一般都可以通过肉眼观测结果是否合理。 关联规则主要用来发现Pattern,最经典的应用是购物篮分析,当然其他类似于购物篮交易数据的案例也可以应用关联规则进行模式发现。

I think so I am 10万+

Python自动化生成HyperMesh Tcl命令流:从几何建模到有限元分析

本文详细介绍了如何利用Python自动化生成HyperMesh的Tcl命令流,实现从几何建模到有限元分析的全流程二次开发。通过封装常用操作函数,工程师可以高效完成参数化建模、自动网格划分及求解器文件输出,显著提升CAE前处理效率与标准化水平。

weixin_29041767的博客 424

深入源码分析RecyclerView缓存复用原理

是Android日常开发中经常使用的控件,了解其源码,明白其中的缓存复用机制是十分有必要的;ღ( ´・ᴗ・` )

故不积跬步无以至千里 2269

【脑电分析系列】第7篇:频域分析基石 — 功率谱密度(PSD)的计算与常见方法对比

本文探讨了脑电图(EEG)频域分析的核心方法——功率谱密度(PSD)。通过对比基于快速傅里叶变换的周期图法和更稳健的Welch's方法,指出后者通过分段、加窗和平均处理,能有效降低估计方差,更适合分析非稳态的脑电信号。文章详细解析了关键参数设置对频率分辨率和统计稳定性的影响,并提供了Python计算实现。PSD分析将复杂的脑电信号转换为直观的频域特征,为研究认知功能和神经疾病提供重要依据。

qq_32516809的博客 1806

深度探秘!Android RecyclerView 缓存机制的底层原理全解析

RecyclerView 的缓存机制是其高性能的关键所在。通过四级缓存(和)的协同工作,RecyclerView 可以高效地复用视图,减少视图的创建和销毁次数,从而提高列表滚动的流畅性和性能。在数据更新时,缓存机制也能根据不同的更新操作进行相应的处理,确保显示的内容与数据一致。同时,通过合理设置缓存大小、优化视图创建和绑定逻辑、利用预取机制和避免不必要的刷新等性能优化方法,可以进一步提升 RecyclerView 的性能。

Android开发领域创作者 2604

黑猫带你学Nand第11篇:万字长文带你了解NandFlash的数据保持原理及特性(Data Retention)

TATTAT:Trap-assistedtunneling,陷阱辅助隧穿,也有叫法称为电荷陷阱现象。这是一种物理现象,它发生在非挥发性存储器,如NAND闪存。在这种效应中,存储在闪存单元浮动栅极上的电荷(电子)由于隧道氧化物中捕获的电荷形成的电场影响,导致电荷通过隧道效应从一个能级隧穿到另一个能级,从而造成电荷泄漏。这种电荷泄漏是导致闪存数据保持问题的主要原因之一,因为它可以改变存储在闪存单元中的电荷量,进而改变其阈值电压水平,最终可能导致数据错误。在NANDFlash中,电荷被存储在浮置栅极上。

黑猫奥利奥的博客 1102

手把手教你学 Simulink (1.3):探索Simulink模块库的奇妙世界

Simulink模块库按照功能分为多个类别,每个类别包含专门用于特定任务的模块。% 打开Simulink库浏览器% 或者以编程方式获取库列表disp('可用的库列表:');end常用模块(Simulink):最基础和最常用的模块信号处理模块(DSP System Toolbox):专业信号处理工具通信模块(Communications Toolbox):通信系统设计控制系统模块(Control System Toolbox):控制系统设计与分析% 创建自定义模块库。

QQ_778132974的博客 384

RecyclerView源码解析(三)——深度解析缓存机制

本系列博客基于com.android.support:recyclerview-v7:26.1.0 1.【进阶】RecyclerView源码解析(一)——绘制流程 2.【进阶】RecyclerView源码解析(二)——缓存机制 3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制 4.【进阶】RecyclerView源码解析(四)——RecyclerView进阶优化使用 5.【...

l_215851356的专栏 261

YOLOv13全网首发:CVPR2026 MixerCSeg | DEGConv方向引导边缘门控,破解细长裂缝检测难题

DEGConv通过方向先验嵌入、边缘增强卷积和动态门控三大机制,针对性解决了YOLO在处理裂缝类细长、多方向、低对比度目标时的特征提取不足问题,显著提升了裂缝检测的精度与鲁棒性。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 442

RecyclerView缓存和复用机制详解

引入依赖库:implementation 'androidx.recyclerview:recyclerview:1.1.0' 一、RecyclerView的介绍 从名字可以看出Recycler是可回收复用的意思,recyclerView就是可回收缓存并复用的View,回收缓存的是itemView,复用的是itemView(itemView就是你为每种type写的R.layout.xxx布局,LayoutInflater.inflate()的那个view),RecyclerView本身就是继承Vie

sunbinkang的博客 2813

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Android RecyclerView 缓存机制深度解析与面试题

深入理解的缓存机制对于优化列表和网格的性能至关重要。通过本文的分析,希望你能在面试中更好地展示你对缓存机制的理解和实践经验。欢迎关注我的公众号AntDream查看更多精彩文章!

AntDream的程序世界 1557

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

让你彻底掌握RecyclerView的缓存机制

前言 RecyclerView这个控件几乎所有的Android开发者都使用过(甚至不用加几乎),它是真的很好用,完美取代了ListView和GridView,而RecyclerView之所以好用,得益于它优秀的缓存机制。关于RecyclerView缓存机制,更是需要我们开发者来掌握的。本文就将先从整体流程看RecyclerView的缓存,再带你从源码角度分析,跳过读源码的坑,最后用一个简单的demo的形式展示出来。在开始RecyclerView的缓存机制之前我们先学习关于ViewHolder的知识。 R

xutaojxx的博客 1627

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

RecyclerView源码解析(二)——缓存机制

本系列博客基于com.android.support:recyclerview-v7:26.1.0 1.【进阶】RecyclerView源码解析(一)——绘制流程 2.【进阶】RecyclerView源码解析(二)——缓存机制 3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制 4.【进阶】RecyclerView源码解析(四)——RecyclerView进阶优化使用 5.【...

l_215851356的专栏 380

【进阶】RecyclerView源码解析(一)——绘制流程

1.【进阶】RecyclerView源码解析(一)——绘制流程 2.【进阶】RecyclerView源码解析(二)——缓存机制 3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制 引言 自从Google出了RecyclerView后,基本上列表的场景已经完全替代了原来的ListView和GridView,现在不仅仅是列表,多样式(俗称盖楼),复杂页...

被代码淹没的小伙子的博客 6968

RecyclerView源码解析(四)——RecyclerView进阶优化使用

本系列博客基于com.android.support:recyclerview-v7:26.1.0 1.【进阶】RecyclerView源码解析(一)——绘制流程 2.【进阶】RecyclerView源码解析(二)——缓存机制 3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制 4.【进阶】RecyclerView源码解析(四)——RecyclerView进阶优化使用 5.【...

l_215851356的专栏 245

【进阶】RecyclerView源码解析(二)——缓存机制

1.【进阶】RecyclerView源码解析(一)——绘制流程 2.【进阶】RecyclerView源码解析(二)——缓存机制 3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制 引言 接着上一篇博客分析完RecyclerView的绘制流程,其实对RecyclerView已经有了一个大体的了解,尤其是RecyclerView和LayoutManag...

被代码淹没的小伙子的博客 2784

Android基础-RecyclerView详解

资料 ItemDecoration深入解析与实战(一)——源码分析 一款不错的流式布局LayoutManager-FlowLayout

AdrianAndroid的专栏 2100

真正带你搞懂RecyclerView的缓存机制,深度解析,值得收藏

开头 程序员面试,除了面试技术外,有的公司经常会问应聘者和技术无关的问题,考验求职者的综合能力,并以此作为是否录用的依据,很多时候这类问题往往没有标准答案,就看求应聘者临场的反应能力如何。 张工是一名Android开发人员,近日到某知名互联网公司面试,面谈到最后时,面试官问了张工这样一个问题: 你觉得现在做Android还有前途吗? 张工被问得有点措手不及。 现在都在说互联网寒冬,尤其是移动端开发市场趋于饱和状态,我之前找工作时,也陆陆续续找了一个月,应该是我技术水平不过关。 现在做Android还有前途吗

m0_57077948的博客 171

【进阶】RecyclerView源码解析(四)——RecyclerView进阶优化使用

1.【进阶】RecyclerView源码解析(一)——绘制流程 2.【进阶】RecyclerView源码解析(二)——缓存机制 3.【进阶】RecyclerView源码解析(三)——深度解析缓存机制 4.【进阶】RecyclerView源码解析(四)——RecyclerView进阶优化使用 上一篇博客比较深度的对RecyclerView的缓存机制进行了分析,分别对...

被代码淹没的小伙子的博客 637
上一篇: 【进阶】RecyclerView源码解析(二)——缓存机制
下一篇: 【进阶】RecyclerView源码解析(四)——RecyclerView进阶优化使用
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值