RecyclerView是Android平台上非常流行的一个控件,用于展示列表数据。它是ListView的升级版,优化了性能和扩展性。其中最重要的一项优化就是缓存机制,这使得RecyclerView更具有高效性。
一、缓存机制概述
RecyclerView中的缓存机制,是指它的item View的缓存。它缓存了RecyclerView中所有item View的实例,不再每次滚动列表时都去创建新的实例,大大减少了耗时和消耗内存。
RecyclerView的缓存机制主要有两种形式:可复用的缓存池和不可复用的缓存池。
二、可复用的缓存池
RecyclerView将创建的item View存储在一个可复用的缓存池中,这个缓存池用于存储所有已离开屏幕(滑出屏幕)的item View。当需要展示一个新的item View时,RecyclerView先从缓存池中查找可复用的item View,如果找到了就直接使用,如果没有找到就创建新的item View。
1、缓存池的实现
public class RecyclerView{ ... final Recycler mRecycler = new Recycler(); ... class Recycler { ... private ArrayListmAttachedScrap = new ArrayList<>(); private ArrayList mCachedViews = new ArrayList<>(); ... } ... }
RecyclerView的缓存池实现在Recycler类中,缓存池主要有两个ArrayList,分别是mAttachedScrap和mCachedViews。
其中,mAttachedScrap是一个临时缓存池,用于存储RecyclerView中所有已离开屏幕的item View;mCachedViews是一个可复用的缓存池,用于存储所有已经被detach(移除父View)的item View。
2、视图回收机制
RecyclerView的视图回收机制,会在item View离开屏幕后,将item View存储在mAttachedScrap中,等待重新使用。每次滚动列表时,会调用RecyclerView的scrollBy()方法,检测所有在屏幕外的item View,将其放置在mAttachedScrap中。同时,在Recycler类中提供了一个方法,用于清空mAttachedScrap中的所有item View:
void clear() { mAttachedScrap.clear(); recycleAndClearCachedViews(); }
三、不可复用的缓存池
在item View因为屏幕滑动被移除屏幕时,RecyclerView并不会将其直接放入mCachedViews缓存池中。而是将这些不可复用的item View标记为“Scrap”,并将它们存储在Recycler中的mChangedScrap中。这些不可复用的item View只能在下次重新创建新的item View之前使用,因为它们的ViewHolder会被重新创建,所有缓存失效。
1、不可复用的缓存池实现
class Recycler { ... ArrayListmChangedScrap = null; int mViewCacheMaxCount = DEFAULT_CACHE_SIZE; ... }
RecyclerView的不可复用的缓存池是mChangedScrap ArrayList,用于存储当前屏幕之外的item View,该缓存池的最大容量受到一个参数mViewCacheMaxCount限制,其默认值是2。
2、Scrap机制实现
RecyclerView的Scrap机制主要包含3个过程:
- 在Recycler的detachAndScrapAttachedViews()方法中,将除了第一个可视的Item以外的所有Item View从RecyclerView中detach(移除父View),并将其存储在mChangedScrap中。
- 在Recycler的getViewForPosition()方法中,首先会从可复用的缓存池mCachedViews中查找是否有可复用的item View。如果没有,就从mAttachedScrap中查找。
- 如果mAttachedScrap中也没有可复用的item View,那么就从mChangedScrap中查找是否有不可复用的item View。如果找到了,就将其重新bind(绑定数据)。如果没有找到,就创建全新的item View。
四、对缓存机制的优化
为了提高RecyclerView的性能,可以对缓存机制进行优化:
1、手动保持ViewHolder的状态
ViewHolder是item View的持有者,保存着item View中的子View的引用,因此ViewHolder内部的状态不像item View那样容易受到RecyclerView的状态改变而改变。在View的复用时,可能会引发一些问题。在ViewHolder中存储该View的状态信息,可以解决这类问题。我们可以在ViewHolder中添加一个保存状态的SparseArray,存储着每一个子View的状态信息。在onBindViewHolder()方法中,将保存的状态赋值给子View即可。
2、减少item View的层级结构
RecyclerView的性能也受到item View的层级结构的影响。当一个item View中含有嵌套的子View层级较多的时候,就容易引起过度绘制、卡顿等问题,降低了RecyclerView的性能。因此,为了提高RecyclerView的性能,item View的层级结构应该尽可能的简单。
3、减小item View的尺寸与数量
RecyclerView的性能受到item View的数量和尺寸的影响,如果item View尺寸太小,数量太多,RecyclerView的性能将会变得很低。因此,我们需要尽可能的减少item View的数量,增加item View的尺寸。另外,设置item View尽可能的大,可以减少item View与布局之间的间隙,使屏幕不容易产生过度绘制。
五、总结
RecyclerView的缓存机制是其性能的重要保证之一。RecyclerView通过可复用的缓存池和不可复用的缓存池,实现了对item View的缓存,避免了每次滚动列表时都去创建新的实例。同时,通过手动保持ViewHolder的状态、减少item View的层级结构和数量、减小item View的尺寸,可以进一步提高RecyclerView的性能。