前言
RecyclerViewc.gif最近在网上看到许多关于 RecyclerView 缓存相关的技术文章,也是在其中看到了许多的知识点,我将之收集了起来进行汇总和整理,利用自己的见解来分析 RecyclerView 的缓存机制的相关问题
优势
RecyclerView1.png首先 RecyclerView 是由 Google 推出来对 GridView 和 ListView 进行取代的列表方案,RecyclerView 本身它是不关心视图相关的问题的,由于 ListView的紧耦合的问题, google 的改进就是 RecyclerView 自身不用参与任何视图有关的问题,它不用在意应该将子 View 放在合适的位置,也不在意如何进行分割这些子 View,更不在意每个子View所显示的外观,本质上来说就是 RecyclerView 它只负责回收和重用的工作
● 能够替代 Listview 和 GridView ,不仅可以加载列表同时也能够加载表格
● 能够支持瀑布流这种高级的显示方式
● 内置了强劲的垃圾回收机制
● 规范了其 Viewholder 的使用
难点
在 RecyclerView 中,是没有 onItemClickListener 方法的,所以只能在适配器中处理事件,如果要从适配器上添加或移除条目,就必须要明确通知适配器。这跟先前的 notifyDataSetChanged 方法有略微不同
整体总结了几点如下:
● Adapter:包装数据集合且为每个条目创建视图
● ViewHolder:对每个用于显示数据条目的子View进行保存
● LayoutManager:在适当的位置放置于每个条目的视图
● ItemDecoration:绘制一些装饰视图在每个条目的视图的周围或上面
● ItemAnimator:在条目被添加、移除或者重排序时对其添加动画效果
缓存分级
RecyclerView 的缓存可以分为四级,也有的人将之分成三级,但大致的理解是一样的
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n91" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public final class Recycler {
//一级缓存mAttachedScrap 和mChangedScrap
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
//二级缓存mCachedViews
final ArrayList<ViewHolder> mCachedViews = new ArrayList<>();
//三级缓存mViewCacheExtension
private ViewCacheExtension mViewCacheExtension;
//四级缓存
RecycledViewPool mRecyclerPool;
}</pre>
一级缓存
● mAttachedScrap 和 mChangedScrap ,用来缓存其还在屏幕内的 ViewHolder
● mAttachedScrap 对当前还在屏幕中的 ViewHolder进行存储;从 id 和 position 来对 ViewHolder进行查找
● mChangedScrap 表达数据已经改变的 ViewHolder 列表, 存储 notifyXXX 方法时必须对 ViewHolder进行改变
二级缓存
● mCachedViews ,是用来缓存移除屏幕之外的 ViewHolder,通常其缓存容量是 2,但可以通过 setViewCacheSize 方法来改变缓存的容量大小,假如mCachedViews 的容量已满,那么则会根据 FIFO 其中的规则来对旧 ViewHolder 进行移除处理
三级缓存
● ViewCacheExtension ,是开发给用户的自定义扩展缓存,是需要用户自己管理 View 的创建和缓存
四级缓存
● RecycledViewPool ,ViewHolder 缓存池,如果在有限的 mCachedViews 中存不下新的 ViewHolder 时,那么就会把 ViewHolder 存入RecyclerViewPool 中
缓存特性
● 根据 Type 来对进行 ViewHolder 查找
● 每个 Type 基本上默认最多缓存 5 个
● 具有可以多个 RecyclerView 共享 RecycledViewPool
代码实现
首先在 Gradle 添加 RecyclerView 的依赖
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n109" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">
dependencies {
...
implementation 'androidx.recyclerview:recyclerview:1.1.0'
}</pre>
在 layout 中使用 RecyclerView
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n111" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_successive_dynasties_huoying"
android:layout_width="match_parent"
android:layout_height="match_parent" /></pre>
在 MainActivity 里面进行实例化 RecyclerView ,并将其设置 LayouManager 和 Adapter
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n113" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">private void initView()
{
recyclerView = findViewById(R.id.rv_successive_dynasties_huoying);
// 线性布局管理器
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
// 用于描述item的适配器
recyclerAdapter = new RecyclerAdapter(huoyingList);
recyclerView.setAdapter(recyclerAdapter);
}</pre>
对 RecyclerViewAdpater 进行实现
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n115" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>
{
private List<Huoying> huoyingList;
public RecyclerAdapter(List<Huoying> huoyingList)
{
this.huoyingList = huoyingList;
}
@NonNull
@Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
ViewHolder viewHolder = new ViewHolder(itemView);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerAdapter.ViewHolder holder, int position)
{
holder.name.setText(huoyingList.get(position).getName());
holder.number.setText(String.valueOf(huoyingList.get(position).getNumber()));
holder.trump.setText(huoyingList.get(position).getTrump());
}
@Override
public int getItemCount()
{
return huoyingList.size();
}
class ViewHolder extends RecyclerView.ViewHolder
{
TextView name; //
TextView number; //
TextView trump; //
public ViewHolder(@NonNull View itemView)
{
super(itemView);
this.name = itemView.findViewById(R.id.tv_name);
this.number = itemView.findViewById(R.id.tv_number);
this.trump = itemView.findViewById(R.id.tv_trump);
}
}
}</pre>
RecyclerAdapter 的关键
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n117" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">RecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
}</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n118" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">void onBindViewHolder(@NonNull RecyclerAdapter.ViewHolder holder, int position) {
}</pre>
onCreateViewHolder 用于对 item.xml 进行机芯实例化,并会以 ViewHolder 的形式呈现
onBindViewHolder 致用在初始和滑动 RecyclerView 时,给予item里面的子控件赋值
在我的理解中, onCreateViewHolde r和 onBindViewHolder 加起来就类似于 ListView adapter 里面的
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n122" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">View getView(int position, View itemView, ViewGroup parent)</pre>
虽然getView()里面既有parent可以用来实例化 item.xml ,又有 position 可以找到item位置来赋值,但是本质上还是如同 onCreateViewHolder 和 onBindViewHolder 的功能!
差异区别就只是在item的表示形式从View变成了 ViewHolder
总结
以上就是关于 RecyclerView 缓存的所有内容
关于RecyclerView的缓存,总的来说,Scrap是屏幕内的缓存一般我们不怎么需要特别注意;Cache可直接拿来复用的缓存,性能高效
ViewCacheExtension 需要开发者自定义的缓存,API设计比较奇怪,慎用
RecycledViewPool 四级缓存,可以避免用户调用onCreateViewHolder 方法,提高性能,在 ViewPager+RecyclerView 的应用场景下可以大有作为
如果喜欢文章中的内容欢迎大家点赞和评论,你们的鼓励将是我前进的动力
有需要文章中的源码,或者想要了解更多关于Android开发相关的进阶资料
欢迎大家在评论区下发留言,或者私信我
网友评论