Android开发经验谈Android开发Android技术知识

来了解 Github 当前最火开源项目 RecyclerView

2022-04-15  本文已影响0人  程序老秃子

前言

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开发相关的进阶资料

欢迎大家在评论区下发留言,或者私信我

混吃等死 派大星.png
上一篇 下一篇

猜你喜欢

热点阅读