ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RecyclerView的缓存复用机制

RecyclerView的缓存复用机制 文章目录RecyclerView的缓存复用机制1. 引入ListView的问题1.1 没有复用的后果1.2 RecyclerView的核心思想2. 四个核心概念3. ViewHolder的作用3.1 什么是ViewHolder3.2 为什么要有ViewHolder哪里看出来用了缓存4. 四级缓存机制4.1 缓存层级4.2 各层级详解5. 从列表滑动看缓存流程5.1 向上滑动时发生了什么5.2 回收过程Item 0滑出5.3 获取过程底部需要Item 86. 预取机制Prefetch6.1 什么是预取6.2 优化效果7. 代码示例7.1 启用预取默认开启7.2 设置缓存大小8. 性能对比9. 易错点总结10. 一句话总结RecyclerView的缓存复用机制1. 引入ListView的问题1.1 没有复用的后果假设一个列表有1000条数据每个Item有一个ImageView加载图片。如果不做任何优化滑动列表时会不断创建新的View// 没有复用的情况publicViewgetView(intposition,ViewconvertView,ViewGroupparent){// 每次滑动都创建一个新ViewViewviewLayoutInflater.from(context).inflate(R.layout.item,parent,false);// 绑定数据...returnview;}滑动1000条就创建1000个View每个View占用内存GC频繁滑动卡顿掉帧严重1.2 RecyclerView的核心思想RecyclerView通过四级缓存强制ViewHolder解决了这个问题核心思想View不销毁只重用2. 四个核心概念概念说明类比ViewType每个Item的类型不同款式的衣服Adapter数据 → View的转换器流水线工人ViewHolder持有View的引用绑定数据衣架Recycler缓存池管理废弃和复用的View仓库3. ViewHolder的作用3.1 什么是ViewHolderViewHolder是一个持有View引用的容器类避免每次绑定数据时重复findViewById// 标准ViewHolder写法staticclassMyViewHolderextendsRecyclerView.ViewHolder{TextViewtvTitle;ImageViewivIcon;publicMyViewHolder(ViewitemView){super(itemView);tvTitleitemView.findViewById(R.id.tv_title);ivIconitemView.findViewById(R.id.iv_icon);}}3.2 为什么要有ViewHolder操作传统方式无ViewHolder有ViewHolder创建Iteminflate布局inflate布局绑定数据每次findViewById直接使用缓存的View引用性能每次都要查找控件只查找一次不用ViewHolder的话每次onBindViewHolder都要走一遍findViewById列表滑动时这个开销就很明显了。哪里看出来用了缓存关键点onBindViewHolder拿到的是已经创建好的 ViewHolder里面的 View 已经 inflate 过了。onBindViewHolder被调用时传入的holder参数是 RecyclerView 从缓存里取出来的它的itemView早就存在tvTitle和ivIcon也早就找到了。onBindViewHolder只负责往这些现成的控件里填新数据。跟没有缓存的写法对比一眼就能看出区别// 没有缓存的写法每次都要重新找控件publicvoidbindView(Viewview,intposition){TextViewtvview.findViewById(R.id.tv_title);// 每次都查一遍tv.setText(dataList.get(position).getTitle());}// 有缓存的写法直接复用publicvoidonBindViewHolder(MyViewHolderholder,intposition){holder.tvTitle.setText(dataList.get(position).getTitle());// 直接拿不用查}4. 四级缓存机制4.1 缓存层级RecyclerView.Recycler │ ┌───────────────────┼───────────────────┐ │ │ │ ┌───▼───┐ ┌─────▼─────┐ ┌──────▼──────┐ │一级缓存│ │ 二级缓存 │ │ 三级缓存 │ │ mAttached│ │ mCacheViews│ │mViewCacheExt│ │ Scrap │ │ (大小2) │ │ (默认为2) │ └────────┘ └───────────┘ └─────────────┘ ┌───────────────────────────────────────────┐ │ 四级缓存RecycledViewPool │ │ 按ViewType缓存默认5个/类型 │ └───────────────────────────────────────────┘4.2 各层级详解层级名称大小存放内容使用场景1级mAttachedScrap不固定正在被使用的ViewHolder布局过程中2级mCachedViews默认为 2离开屏幕但还保留的View滑动回收3级mViewCacheExtension开发者自定义自定义缓存特殊需求4级RecycledViewPool每类 5 个按ViewType缓存的View跨RecyclerView共享5. 从列表滑动看缓存流程5.1 向上滑动时发生了什么屏幕可见区域 ┌─────────────────────────┐ │ Item 0 (消失) │ ← 顶部Item滑出屏幕 ├─────────────────────────┤ │ Item 1 │ │ Item 2 │ │ Item 3 │ │ Item 4 │ ├─────────────────────────┤ │ Item 5 │ │ Item 6 │ │ Item 7 │ │ Item 8 (底部新出现) │ ← 底部需要显示新Item └─────────────────────────┘5.2 回收过程Item 0滑出Item 0 离开屏幕 │ ▼ 检查mCachedViews是否已满默认2个 │ ├─ 未满 → 放入mCachedViews2级缓存 │ 完整保留View下次复用无需重新创建 │ └─ 已满 → 把mCachedViews中最旧的移到RecycledViewPool4级缓存 View被回收重新创建时再inflate5.3 获取过程底部需要Item 8需要显示Item 8 │ ▼ 1. 从mAttachedScrap查找1级 │ 正在使用的View一般是布局过程中 ▼ 2. 从mCachedViews查找2级 │ 最近滑出屏幕的View完整保留 ▼ 3. 从mViewCacheExtension查找3级一般不用 ▼ 4. 从RecycledViewPool查找4级 │ 按ViewType匹配需要重新绑定数据 ▼ 5. 以上都没有 → 创建新的ViewHolder并绑定数据每次查找都要先找1级1级没有找2级2级没有找3级都没有才找4级或重新创建。6. 预取机制Prefetch6.1 什么是预取RecyclerView在滑动时会提前准备即将出现的Item滑动方向 → ↓ 当前可见 [Item 0] [Item 1] [Item 2] [Item 3] 预取任务 提前准备 Item 4下一次滑动可能看到在用户手指还没滑到那个位置时系统就已经提前创建好Item放在缓存里了等到真正要显示的时候直接拿而不是现场创建。6.2 优化效果机制作用预取提前创建/绑定View减少滑动时的卡顿异步在空闲时执行不影响UI主线程7. 代码示例7.1 启用预取默认开启RecyclerViewrecyclerViewfindViewById(R.id.recyclerView);// 默认已启用不需要额外配置7.2 设置缓存大小// 设置mCachedViews大小默认2recyclerView.setItemViewCacheSize(10);// 设置RecycledViewPool大小RecyclerView.RecycledViewPoolpoolnewRecyclerView.RecycledViewPool();pool.setMaxRecycledViews(viewType,10);recyclerView.setRecycledViewPool(pool);8. 性能对比场景无复用有复用滑动1000条创建View次数1000次约屏幕可见数缓存数每次绑定数据findViewById每次都要只创建时一次内存占用越来越高稳定GC频率高低9. 易错点总结易错点说明不同ViewType的View不能复用不同布局无法互相复用RecycledViewPool不区分ViewType的只按viewType区分不同类型的View独立缓存setItemViewCacheSize太大缓存太多会导致内存浪费忘记调用adapter.notifyDataSetChanged数据变化后界面不更新10. 一句话总结RecyclerView的缓存复用机制就是四级缓存依次查找mAttachedScrap当前正在用的mCachedViews刚滑出去的完整保留mViewCacheExtension开发者自定义RecycledViewPool按类型缓存需要重新创建能复用就不创建减少频繁布局和内存分配让滑动更流畅。
返回列表