文章目录

  • Recycler
  • 四级缓存
    • 屏幕内缓存 mAttachedScrap 和 mChangedScrap
      • 进入缓存
    • 屏幕外缓存 mCachedViews
      • 滚动
    • 缓存池 RecycledViewPool
    • 优化
      • notifyDataSetChanged
      • setHasFixedSize

Recycler承担了RecyclerView中的缓存功能,其中定义的5个集合代表了四个缓存层级。

Recycler

//一级缓存
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
//二级缓存
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
///三级缓存
private ViewCacheExtension mViewCacheExtension;
//四级缓存
RecycledViewPool mRecyclerPool;
//默认mCachedViews的最大缓存数量
static final int DEFAULT_CACHE_SIZE = 2;

关于四级缓存的优先级,可以看Recycler中的以下方法:

ViewHolder tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) {//省略其他代码....ViewHolder holder = null;// 1) Find by position from scrap/hidden list/cacheif (holder == null) {holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);}if (holder == null) {// 2) Find from scrap/cache via stable ids, if existsif (mAdapter.hasStableIds()) {holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun);if (holder != null) {// update positionholder.mPosition = offsetPosition;fromScrapOrHiddenOrCache = true;}}if (holder == null && mViewCacheExtension != null) {// We are NOT sending the offsetPosition because LayoutManager does not// know it.final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);if (view != null) {holder = getChildViewHolder(view);}}if (holder == null) { // fallback to poolholder = getRecycledViewPool().getRecycledView(type);}if (holder == null) {holder = mAdapter.createViewHolder(RecyclerView.this, type);}}return holder;}

用图片的形式更加直观一些:

缓存有两个边界条件:进和出。

四级缓存

屏幕内缓存 mAttachedScrap 和 mChangedScrap

scrap仅仅在layout期间不为空。当LayoutManager开始 layout 的时候(pre-layout 或 post-layout),会将所有的viewHolder都放到scrap中。然后一个个再取回来,除非有些view发生了变化。

进入缓存

   LinearLayoutManager.onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
-> LayoutManager.detachAndScrapAttachedViews(Recycler recycler)
-> LayoutManager.scrapOrRecycleView(Recycler recycler, int index, View view)
-> Recycler.scrapView(View view)
void scrapView(View view) {final ViewHolder holder = getChildViewHolderInt(view);if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {// 标记为移除或失效的 || 没有改变 || item 无动画或动画不复用mAttachedScrap.add(holder);} else {// 放入 mChangedScrapmChangedScrap.add(holder);}
}

屏幕外缓存 mCachedViews

mCachedViews是一个 ArrayList 类型集合,不区分 viewHolder 的类型,大小限制为2,可以使用 setItemViewCacheSize()这个方法调整它的大小。

滚动

当发生滚动时,方法调用流程

RecyclerView
-> onTouchEvent(MotionEvent e)
-> scrollByInternal(int x, int y, MotionEvent ev)
-> scrollStep(int dx, int dy, @Nullable int[] consumed)
LinearLayoutManager
-> scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,RecyclerView.State state)
-> scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state)
-> fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable)
-> recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState)
-> recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace)
-> recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex)
RecyclerView.LayoutManager
-> removeAndRecycleViewAt(int index, @NonNull Recycler recycler)
RecyclerView.Recycler
-> recycleView(@NonNull View view)
-> recycleViewHolderInternal(ViewHolder holder)
-> recycleCachedViewAt(int cachedViewIndex)
-> addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled)
RecyclerView.RecycledViewPool
-> putRecycledView(ViewHolder scrap)

在下面的方法中,判断了Holder是否需要回收

private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {final int limit = scrollingOffset - noRecycleSpace;final int childCount = getChildCount();//依次判断childView是否已经离开屏幕可见区域,并进行回收for (int i = 0; i < childCount; i++) {View child = getChildAt(i);if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {// stop hererecycleChildren(recycler, 0, i);return;}}
}

回收时,如果mCachedViews已满,移除mCachedViews中的第一个元素,将其放入RecycledViewPool

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)) {// mCachedViews的默认缓存数量是2,如果缓存已满,移除掉最老的Holer,为新的Holder让出空间int cachedViewSize = mCachedViews.size();if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {//移除mCachedViews中的第一个元素,将其放入RecycledViewPoolrecycleCachedViewAt(0);cachedViewSize--;}int targetCacheIndex = cachedViewSize;mCachedViews.add(targetCacheIndex, holder);cached = true;}if (!cached) {// 不能放进 mCacheViews 的放 RecyclerViewPooladdViewHolderToRecycledViewPool(holder, true);recycled = true;}}
}void recycleCachedViewAt(int cachedViewIndex) {ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);addViewHolderToRecycledViewPool(viewHolder, true);mCachedViews.remove(cachedViewIndex);
}public void putRecycledView(ViewHolder scrap) {final int viewType = scrap.getItemViewType();final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;//如果RecycledViewPool的该类型集合已满(默认最大值 = 5),不做任何处理if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {return;}scrap.resetInternal();scrapHeap.add(scrap);
}

缓存池 RecycledViewPool

RecycledViewPool,它储存了各个类型的 viewHolder。最大数量为5,可以通过 setMaxRecycledViews() 方法来设置每个类型储存的容量。可以多个列表公用一个 RecycledViewPool,使用 setRecycledViewPool() 方法。

优化

notifyDataSetChanged

当调用这个方法时,所有的ViewHolder被标记为无效,意味着全部将进入RecycledViewPool缓存,其他缓存为空,因为RecycledViewPool的默认缓存最大值只有5,当RecyclerView展示的item数量超过5时,无法取得缓存,会选择重新创建,执行onCreateViewHolder。考虑到此,刷新数据尽量不要调用notifyDataSetChanged,使用其他方法代替,或者,调整RecycledViewPool的最大缓存数量

setHasFixedSize

如果RecyclerView的尺寸不受adapter的影响,RecyclerView可以做一些优化。
RecyclerView仍可以基于其他因素更改其尺寸(例如,父级的大小),但大小的计算不依赖于子项的大小或adapter的内容(adapter中的数量除外)。
设置为true,可避免adapter内容更改时整个布局无效。

简而言之,当设置为true的时候,adapter将不再影响recyclerView的大小,由此可以避免刷新recyclerView的时候,重新布局计算大小。

#RecyclerView
public void setHasFixedSize(boolean hasFixedSize) {mHasFixedSize = hasFixedSize;
}

当调用adapter.notifyDataSetChanged一类方法刷新界面时,会执行到下面的对应方法:

private class RecyclerViewDataObserver {@Overridepublic void onChanged() {processDataSetCompletelyChanged(true);if (!mAdapterHelper.hasPendingUpdates()) {requestLayout();}}@Overridepublic void onItemRangeChanged(int positionStart, int itemCount, Object payload) {if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {triggerUpdateProcessor();}}@Overridepublic void onItemRangeInserted(int positionStart, int itemCount) {if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {triggerUpdateProcessor();}}@Overridepublic void onItemRangeRemoved(int positionStart, int itemCount) {if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {triggerUpdateProcessor();}}@Overridepublic void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {triggerUpdateProcessor();}}void triggerUpdateProcessor() {if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);} else {mAdapterUpdateDuringMeasure = true;requestLayout();}}}

onChanged和其他方法的区别在于,其他方法都调用了triggerUpdateProcessortriggerUpdateProcessor使用mHasFixedSize做了一些判断,适当的条件下才去调用requestLayoutonChanged则直接调用了requestLayout。因此想要跳过requestLayout,除了不调用onChanged外,mHasFixedSize必须设置为true。

mHasFixedSize为true的情况下,依然可以通过调用onChanged,也就是notifyDataSetChanged,调整recyclerView的大小。

参考:
https://blog.csdn.net/weixin_43130724/article/details/90068112?spm=1001.2014.3001.5502

Recycler缓存机制相关推荐

  1. RecyclerView缓存机制(回收些啥?)

    这是RecyclerView缓存机制系列文章的第二篇,系列文章的目录如下: RecyclerView缓存机制(咋复用?) RecyclerView缓存机制(回收些啥?) RecyclerView缓存机 ...

  2. recyclerview item点击无效_让你彻底掌握RecyclerView的缓存机制

    点击上方蓝字关注 ?? 来源:肖邦kakahttps://www.jianshu.com/p/3e9aa4bdaefd 前言 RecyclerView这个控件几乎所有的Android开发者都使用过(甚 ...

  3. RecyclerView详解一,使用及缓存机制

    本文大致会先讲解RecyclerView的基础知识及使用,最后会深入讲解一点原理.当然,本人知识水平有限哈,太深入的东西我现在还没接触到,还请大家包容,阿里嘎多~ 一.RecyclerView的历史与 ...

  4. RecyclerView缓存机制(scrap view)

    这是RecyclerView缓存机制系列文章的第四篇,系列文章的目录如下: RecyclerView缓存机制(咋复用?) RecyclerView缓存机制(回收些啥?) RecyclerView缓存机 ...

  5. Android RecyclerView 绘制流程及Recycler缓存

    前言 RecyclerView源码一万多行,想全部读懂学会挺麻烦的,感兴趣的可以自己去瞅瞅,这篇文章重点来看下 RecyclerView是如何一步步将每一个 ItemView 显示到屏幕上,然后再分析 ...

  6. Android RecyclerView 绘制流程及Recycler缓存,Android开发者必看避坑指南

    - mState.mIsMeasuring = false; if (mState.mLayoutStep == State.STEP_START) { dispatchLayoutStep1(); ...

  7. Django缓存机制

    Django缓存机制三个粒度:1 全站缓存 settings.py 全局配置文件用中间件:MIDDLEWARE = [# 'django.middleware.cache.UpdateCacheMid ...

  8. MyBatis复习笔记6:MyBatis缓存机制

    MyBatis缓存机制 MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制.缓存可以极大的提升查询效率. MyBatis系统中默认定义了两级缓存. 一级缓存和二级缓存. 默认情 ...

  9. java设置缓存机制

    2019独角兽企业重金招聘Python工程师标准>>> java设置缓存机制 所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实 ...

最新文章

  1. Android 自定义Toast实现多次触发只会显示一次toast
  2. Atitit main函数的ast分析  数组参数调用的ast astview解析
  3. 他们爬了7000家创业公司数据,发现这些领域没那么容易应用AI
  4. Spark-ML-数据获取/处理/准备
  5. 分库分表技术演进最佳实践-修订篇
  6. 【Apache 】 遇到的问题
  7. python lib head,使用Python 2中的urllib2发出HTTP HEAD请求
  8. android/IOS SDK怎么判断用户是否安装了微信/QQ
  9. ITK:侵蚀二进制图像
  10. 为什么手机游戏手柄没有流行起来?
  11. 面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
  12. 关于对象的引用作为参数,可以直接访问私有成员的问题
  13. wpf使用入式mysql_c#之wpf:从mysql数据库中数据绑定到页面上
  14. Bootstrap采样方法的python实现
  15. oracle事件跟踪器使用,Oracle 10046跟踪事件操作步骤
  16. java接口测试工具_接口模拟测试利器,moco server工具的介绍
  17. 共享hp无线扫描到计算机,共享HP / HP扫描仪的OpenWrt路由器
  18. 三维模型格式转换神器-assimp
  19. RAID磁盘阵列与配置(详细)
  20. Python文本相似度识别(附图形化界面)

热门文章

  1. Cf手游穿越火线:枪战王者氪金严重?加入吃鸡模式变得不伦不类
  2. LeetCode 面试题 17.07. 婴儿名字
  3. iis5.1安装包下载 及安装过程
  4. C#操作Word之在书签处写入特殊字符
  5. charm zaza functional test (by quqi99)
  6. 圈子决定人生,靠近什么样的人
  7. C - Mine Sweeper
  8. 如何利用微博的微活动进行引流?
  9. 女生的第一次,往往都会很害羞
  10. pywinauto客户端自动化---模拟鼠标操作