当前位置:首页 > 科技  > 软件

RecyclerView的缓存机制及使用策略

来源: 责编: 时间:2024-06-28 17:15:54 266观看
导读RecyclerView的缓存机制是为了提高列表滚动时的性能。采用了多级缓存策略来存储和复用视图(View),减少视图的创建和销毁,进而减少内存分配和GC的频率。缓存层级负责回收和复用ViewHolder的类是Recycler,负责缓存的主要就是

RecyclerView的缓存机制是为了提高列表滚动时的性能。采用了多级缓存策略来存储和复用视图(View),减少视图的创建和销毁,进而减少内存分配和GC的频率。tRO28资讯网——每日最新资讯28at.com

缓存层级

负责回收和复用ViewHolder的类是Recycler,负责缓存的主要就是这个类的几个成员变量。tRO28资讯网——每日最新资讯28at.com

public final class Recycler {    // 存放可见范围内的 ViewHolder (但是在 onLayoutChildren 的时候,会将所有 View 都会缓存到这), 从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。    final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();    // 存放可见范围内并且数据发生了变化的 ViewHolder,从这里复用的 ViewHolder 需要重新绑定数据。    ArrayList<ViewHolder> mChangedScrap = null;    // 存放 remove 掉的 ViewHolder,从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。    final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();     // 默认值是 2    private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;     // 默认值是 2    int mViewCacheMax = DEFAULT_CACHE_SIZE;     // 存放 remove 掉,并且重置了数据的 ViewHolder,从这里复用的 ViewHolder 需要重新绑定数据。 // 默认值大小是 5     RecycledViewPool mRecyclerPool;     // 自定义的缓存    private ViewCacheExtension mViewCacheExtension;     }

RecyclerView的缓存机制主要由四个部分组成,它们按照从高到低的优先级排列:tRO28资讯网——每日最新资讯28at.com

  1. 「Scrap缓存(Scrap Heap)」

包括mAttachedScrap和mChangedScrap,也称为屏内缓存,因为它们主要用于保存屏幕内当前可见或者即将可见的ViewHolder。tRO28资讯网——每日最新资讯28at.com

mAttachedScrap:存放的是已添加到RecyclerView但与RecyclerView临时分离(例如在滚动或布局调整过程中)的ViewHolder。tRO28资讯网——每日最新资讯28at.com

mChangedScrap:存放的是数据已改变但尚未重新绑定数据的ViewHolder,通常用于动画播放等场景。tRO28资讯网——每日最新资讯28at.com

  1. 「Cache缓存(mCachedViews)」

又称离屏缓存,用于保存最新被移除(remove)但尚未被回收的ViewHolder。tRO28资讯网——每日最新资讯28at.com

缓存的大小是有限制的,默认最大数量为2(由DEFAULT_CACHE_SIZE定义)。tRO28资讯网——每日最新资讯28at.com

当需要展示新视图时,会首先检查Cache缓存中是否有可用的ViewHolder。tRO28资讯网——每日最新资讯28at.com

  1. tRO28资讯网——每日最新资讯28at.com

    「ViewCacheExtension」tRO28资讯网——每日最新资讯28at.com

    tRO28资讯网——每日最新资讯28at.com

为开发者预留的缓存池,允许开发者自定义缓存策略,存储更多的或特定类型的ViewHolder。tRO28资讯网——每日最新资讯28at.com

开发者可以通过实现ViewCacheExtension接口来扩展缓存功能。tRO28资讯网——每日最新资讯28at.com

  1. tRO28资讯网——每日最新资讯28at.com

    「RecycledViewPool(mRecyclerPool)」tRO28资讯网——每日最新资讯28at.com

    tRO28资讯网——每日最新资讯28at.com

终极的回收缓存池,用于存放被标识为废弃(即其他缓存池不再需要的)的ViewHolder。tRO28资讯网——每日最新资讯28at.com

这些ViewHolder已经被抹除了数据,需要重新绑定数据才能使用。tRO28资讯网——每日最新资讯28at.com

RecycledViewPool会根据不同的item类型创建不同的List来存储ViewHolder。tRO28资讯网——每日最新资讯28at.com

缓存使用策略

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,        RecyclerView.State state, boolean stopOnFocusable) {    // max offset we should set is mFastScroll + available    final int start = layoutState.mAvailable;    //首选该语句块的判断,判断当前状态是否为滚动状态,如果是的话,则触发 recycleByLayoutState 方法    if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {        // TODO ugly bug fix. should not happen        if (layoutState.mAvailable < 0) {            layoutState.mScrollingOffset += layoutState.mAvailable;        }        // 分析1----回收        recycleByLayoutState(recycler, layoutState);        }    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {        //分析2----复用        layoutChunk(recycler, state, layoutState, layoutChunkResult);    }}// 分析1----回收 // 通过一步步追踪,我们发现最后调用的是 removeAndRecycleViewAt() public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {    final View view = getChildAt(index);    //分析1-1    removeViewAt(index);    //分析1-2    recycler.recycleView(view);}// 分析1-1// 从 RecyclerView 移除一个 View public void removeViewAt(int index) {    final View child = getChildAt(index);    if (child != null) {        mChildHelper.removeViewAt(index);    }}//分析1-2 // recycler.recycleView(view) 最终调用的是 recycleViewHolderInternal(holder) 进行回收 VH (ViewHolder)void recycleViewHolderInternal(ViewHolder holder) {    if (forceRecycle || holder.isRecyclable()) {        //判断是否满足放进 mCachedViews         if (mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID| ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)){            // 判断 mCachedViews 是否已满            if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {                // 如果满了就将下标为0(即最早加入的)移除,同时将其加入到 RecyclerPool 中                recycleCachedViewAt(0);                cachedViewSize--;                }              mCachedViews.add(targetCacheIndex, holder);            cached = true;            }        //如果没有满足上面的条件,则直接存进 RecyclerPool 中            if (!cached) {            addViewHolderToRecycledViewPool(holder, true);            recycled = true;         }      }}//分析2void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,        LayoutState layoutState, LayoutChunkResult result) {    //分析2-1    View view = layoutState.next(recycler);    if (layoutState.mScrapList == null) {        if (mShouldReverseLayout == (layoutState.mLayoutDirection                == LayoutState.LAYOUT_START)) {            //添加到 RecyclerView 上            addView(view);        } else {            addView(view, 0);        }    }}//分析2-1//layoutState.next(recycler) 最后调用的是 tryGetViewHolderForPositionByDeadline() 这个方法正是 复用 核心的方法ViewHolder tryGetViewHolderForPositionByDeadline(int position,        boolean dryRun, long deadlineNs) {    // 0) If there is a changed scrap, try to find from there    // 例如:我们调用 notifyItemChanged 方法时    if (mState.isPreLayout()) {        // 如果是 changed 的 ViewHolder 那么就先从 mChangedScrap 中找        holder = getChangedScrapViewForPosition(position);        fromScrapOrHiddenOrCache = holder != null;    }    // 1) Find by position from scrap/hidden list/cache    if (holder == null) {        //如果在上面没有找到(holder == null),那就尝试从通过 pos 在 mAttachedScrap/ mHiddenViews / mCachedViews 中获取        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);    }    if (holder == null) {        // 2) Find from scrap/cache via stable ids, if exists        if (mAdapter.hasStableIds()) {            //如果在上面没有找到(holder == null),那就尝试从通过 id 在 mAttachedScrap/ mCachedViews 中获取            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),        }        if (holder == null && mViewCacheExtension != null) {            //这里是通过自定义缓存中获取,忽略        }        //如果在上面都没有找到(holder == null),那就尝试在 RecycledViewPool 中获取        if (holder == null) { // fallback to pool            holder = getRecycledViewPool().getRecycledView(type);            if (holder != null) {                //这里拿的是,要清空数据的                holder.resetInternal();            }        }        //如果在 Scrap / Hidden / Cache / RecycledViewPool 都没有找到,那就只能创建一个了。        if (holder == null) {            holder = mAdapter.createViewHolder(RecyclerView.this, type);        }    }    return holder;}
  • 「RecyclerView滚动时」:首先移除滑出屏幕的item,并将这些ViewHolder存入Cache缓存(mCachedViews)。如果Cache缓存已满,则将更旧的ViewHolder存入RecycledViewPool。
  • 「数据更新时」:如果屏幕内的某个item数据发生变化,但ViewHolder仍然可见,那么这个ViewHolder会被放入mChangedScrap。当需要重新绑定数据时,会从这个缓存中取出ViewHolder。
  • 「删除item时」:被删除的item对应的ViewHolder首先会进入Scrap缓存,然后可能会被移入Cache缓存或RecycledViewPool。

「注意」:当RecyclerView不再需要某个ViewHolder时(例如,当列表项被完全移出屏幕并且缓存已满时),ViewHolder会被放入RecycledViewPool并最终可能被系统回收。tRO28资讯网——每日最新资讯28at.com

缓存机制的好处

  • 「减少视图创建和销毁」:通过复用已有的ViewHolder,大大减少视图的创建和销毁次数,从而节省内存和提高性能。
  • 「优化滚动性能」:当滚动列表时,由于大部分视图都可以从缓存中快速获取,可以保持流畅的滚动体验。
  • 「降低GC频率」:由于减少了视图的创建和销毁,也降低了GC的频率,进一步提高了应用的性能。

tRO28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-97296-0.htmlRecyclerView的缓存机制及使用策略

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 一个合理的前端应用文件结构

下一篇: 一个诡异的Json反序列化问题

标签:
  • 热门焦点
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    Redmi的后性能时代战略发布会今天下午如期举办,在本次发布会上,Redmi公布了多项关于和联发科的深度合作,以及新机K60 Ultra在软件和硬件方面的特性,例如:“K60 至尊版,双芯旗舰
  • Automa-通过连接块来自动化你的浏览器

    1、前言通过浏览器插件可实现自动化脚本的录制与编写,具有代表性的工具就是:Selenium IDE、Katalon Recorder,对于简单的业务来说可快速实现自动化的上手工作。Selenium IDEKat
  • 一文看懂为苹果Vision Pro开发应用程序

    译者 | 布加迪审校 | 重楼苹果的Vision Pro是一款混合现实(MR)头戴设备。Vision Pro结合了虚拟现实(VR)和增强现实(AR)的沉浸感。其高分辨率显示屏、先进的传感器和强大的处理能力
  • 分布式系统中的CAP理论,面试必问,你理解了嘛?

    对于刚刚接触分布式系统的小伙伴们来说,一提起分布式系统,就感觉高大上,深不可测。而且看了很多书和视频还是一脸懵逼。这篇文章主要使用大白话的方式,带你理解一下分布式系统
  • 2纳米决战2025

    集微网报道 从三强争霸到四雄逐鹿,2nm的厮杀声已然隐约传来。无论是老牌劲旅台积电、三星,还是誓言重回先进制程领先地位的英特尔,甚至初成立不久的新
  • iQOO 11S评测:行业唯一的200W标准版旗舰

    【Techweb评测】去年底,iQOO推出了“电竞旗舰”iQOO 11系列,作为一款性能强机,该机不仅全球首发2K 144Hz E6全感屏,搭载了第二代骁龙8平台及144Hz电竞
  • 到手价3099元起!iQOO Neo8 Pro今日首销:安卓性能最强旗舰

    5月23日,iQOO如期举行了新品发布会,全新的iQOO Neo8系列也正式与大家见面,包含iQOO Neo8和iQOO Neo8 Pro两个版本,其中标准版搭载高通骁龙8+,而Pro版更
  • OPPO K11样张首曝:千元机影像“卷”得真不错!

    一直以来,OPPO K系列机型都保持着较为均衡的产品体验,历来都是2K价位的明星机型,去年推出的OPPO K10和OPPO K10 Pro两款机型凭借各自的出色配置,堪称有
Top