(重要)RecycleView的缓存机制

2019-05-21 12:43:26

参考资料RecycleView的缓存机制

一、Recyclerview的缓存类

RecycleView的四级缓存是由三个类共同作用完成的,Recycler、RecycledViewPool和ViewCacheExtension。Recycler用于管理已经废弃或者与RecyclerView分离的ViewHolder,这里面有两个重要的成员,为可以看见的屏幕的内部缓存成员mAttachedScrap、mChangedScrap和滑出屏幕外的外部缓存成员mCachedViews二者共同完成ViewHolder的缓存;RecycledViewPool类是用来缓存整体所有的ViewHolder,是对mCachedViews缓存的补充;ViewCacheExtension是扩展内的缓存对象,默认不加载,需实现方法getViewForPositionAndType(Recycler recycler, int position, int type)来实现自己的缓存。接下来对四级缓存一步步介绍。


二、Recycler屏幕内部缓存

上面说过屏幕内部缓存包括两个重要的成员mAttachedScrap、mChangedScrap。mChangedScrap表示数据已经改变的viewHolder列表,mAttachedScrap表示未与RecyclerView分离的ViewHolder列表,他是Recycleview的一级缓存,缓存查找中不会调用Recycleview方法onBindViewHolder()。


    public final class Recycler {

        //一级缓存中用来存储屏幕中显示的ViewHolde

        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();

        ArrayList<ViewHolder> mChangedScrap = null;

        //二级缓存中用来存储屏幕外的缓存

        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();

        //暂可忽略 mAttachedScrap的不可变视图

        private final List<ViewHolder> mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);

        //当前屏幕外缓存大小,数量为2

        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;

        int mViewCacheMax = DEFAULT_CACHE_SIZE;

        //四级缓存当二级缓存超过2时候启用

        RecycledViewPool mRecyclerPool;

        //三级缓存扩展类(默认不启用)

        private ViewCacheExtension mViewCacheExtension;

 

        static final int DEFAULT_CACHE_SIZE = 2;

     }

1、内部缓存的添加


    void scrapView(View view) {

         //根据View获取ViewHolder

        final ViewHolder holder = getChildViewHolderInt(view);

        //判断该ViewHolder是否分离

        if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)

                || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {

            if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {

                throw new IllegalArgumentException("Called scrap view with an invalid view."

                        + " Invalid views cannot be reused from scrap, they should rebound from"

                        + " recycler pool." + exceptionLabel());

            }

            //未分离的ViewHolder设置false

            holder.setScrapContainer(this, false);

            //添加到未分离的集合mAttachedScrap里面

            mAttachedScrap.add(holder);

        } else {

            if (mChangedScrap == null) {

                mChangedScrap = new ArrayList<ViewHolder>();

            }

            //如果已经分离设置ViewHolder为true

            holder.setScrapContainer(this, true);

            //添加到已经分离的集合mChangedScrap里面

            mChangedScrap.add(holder);

        }

    }

三、Recycler屏幕外部缓存

屏幕的外部缓存是二级缓存,使用的缓存容器变量是mCachedViews,缓存最近回收的ViewHolder,缓存复用时必须匹配position,这个集合里存的 ViewHolder 的原本数据信息都在,所以可以直接添加到 RecyclerView 中显示,不需要再次重新onBindViewHolder()。默认大小是2,最大的缓存大小默认为2个ViewHolder,可以通过Recycleview的setViewCacheSize方法进行设置缓存大小。


public void setViewCacheSize(int viewCount) {

mRequestedCacheMax = viewCount;

updateViewCacheSize();

}

如果缓存满了的话,就会移除最后一个缓存的缓存加到四级缓存的缓冲池中:


void recycleCachedViewAt(int cachedViewIndex) {

   //cachedViewIndex为缓存容器mCachedViews的mCachedViews.size() - 1

ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);

   //添加进缓存池

addViewHolderToRecycledViewPool(viewHolder, true);

//移除缓存容器mCachedViews中该ViewHolder

mCachedViews.remove(cachedViewIndex);

}

四、ViewCacheExtension缓存

ViewCacheExtension缓存是自定义的缓存三级缓存,是一个Recycleview的一个抽象的静态内部内留,给开发者开发,需要直线getViewForPositionAndType抽象方法,默认不会加载,这里不详细说明:


   public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);

五、RecycledViewPool缓存

    void                                      clear() 清空缓存池

    RecyclerView.ViewHolder    getRecycledView(int viewType)    得到一个viewType类型的Item

    void                                      putRecycledView(RecyclerView.ViewHolder scrap)    把viewType类型的Item放入缓存池

    void                                    setMaxRecycledViews(int viewType, int max)设置对应viewType类型的Item的最大缓存数量


    这里看看常用的setMaxRecycledViews方法:


    RecyclerView.RecycledViewPool pool = recyclerView.getRecycledViewPool();

    pool.setMaxRecycledViews(0,10);

    //如果LayoutManager是LinearLayoutManager GridLayoutManager,则需要setRecycleChildrenOnDetach(true)

    //layoutManager.setRecycleChildrenOnDetach(true);

    recyclerView.setRecycledViewPool(pool);

Recycleview的四级缓存图总结如下:




 


六、Recycleview的缓存引起的错乱问题

Recycleview的缓存虽然功能强大,但是有些时候缓存复用能导致页面布局错位,异常,问题出现后通常有以下方法解决:


1、ViewHolder的setIsRecyclable方法


使用ViewHolder的setIsRecyclable方法可以使禁止该ViewHolder的缓存,每个页面都是新的,这种方法简单、快捷,但是也十分暴力数据量大的话会导致滑动顿卡,数据量小的话效果不错。他的原理是通过设置标志位,使其忽略一切缓存。


holder.setIsRecyclable(false);

2、Recycleview的setViewCacheSize方法


通过设置setViewCacheSize的大小可以扩大或者缩小屏幕外的缓存数量,来改善异常问题。


     recyclerView.setItemViewCacheSize(0);//禁用屏幕外缓存,直接使用RecycledViewPool缓存

 

     recyclerView.setItemViewCacheSize(20);//扩大屏幕外缓存

3、 Recycleview的setRecycledViewPool方法


通过设置指定类型的ViewHolder的缓存池中能缓存的数量来限制。


    pool.setMaxRecycledViews(0,0);//TYPE为0,缓存数量为0

    recyclerView.setRecycledViewPool(pool);//给Recycleview设置缓存池对象

4、通过新增一个boolean类型变量维护在接口返回数据Bean类中


每次选中或者点击设置数据的布尔值为true,非选中或者执行状态置为false,这样每次的UI展示都是根据接口维护数据来判断,不会引起错位问题。


5、通过内部维护Map集合


将选中和非选中的状态存储在Map集合中,位置position作为key,状态作为value,每次UI展示操作都从集合中获取position对应,的值,进行状态重置。


总结:

RecyclerView的四级缓存包括:


一级缓存:mAttachedScrap、mChangedScrap

二级缓存:mCacheViews

三级缓存:mViewCacheExtension

四级缓存:mRecyclerPool

一级缓存为屏幕内缓存,二级缓存为屏幕外缓存,三级缓存为自定义缓存,四级缓存为缓存池缓存。一二三即缓存直接不需要重新绑定View,四级缓存需要绑定Holder设置数据。禁用缓存可以使用ViewHolder的setIsRecyclable方法。




  • 2019-05-18 12:38:41

    android 快速实现夜间模式

    最近项目中遇到了一个问题,夜间模式在8.0以上的手机中不起作用,查看了一下原因,是夜间模式实现方法的问题。分两种情况介绍一下

  • 2019-05-18 12:40:35

    Android夜间模式的几种实现

    通过增加一层遮光罩来实现。效果不是很理想,但是好用,毕竟很多手机都有自己的夜间模式了

  • 2019-05-19 02:25:15

    php使用TCPDF生成PDF文件教程

    orientation属性用来设置文档打印格式是“Portrait”还是“Landscape”。 Landscape为横式打印,Portrait为纵向打印

  • 2019-05-21 11:46:05

    RecyclerView 加动画的坑

    然后加到recyclerView上,我是在adapter上加的。Adapter的holder复用相信大家也都很熟悉了,这个在绘制效率的提高上很重要,也很容易发现一个问题,就是内容混乱的复用。所以常见的处理就是对view加上tag来多次判断,对于visibility之类的设置一定是if...else的写法,光有if是不可以的。

  • 2019-05-21 11:54:10

    Android中如何设置字体大小

    首先要强调一点,某些用户可能存在视力障碍甚至几近失明,他们无法顺利阅读一般尺寸下的文字内容。Android操作系统意识到了这些情况,并提供了一套显示辅助功能,使得用户可以根据自己的使用习惯随意缩放设备中文本字体的大小。 要在设备上修改字体设定,首先启用“设定”应用,然后选择“显示”项下的“字体尺寸”。用户可以在设备预置的四种文本字体尺寸中选择适合自己的方案(包含小、正常、大、超大),详见图一。

  • 2019-05-21 12:34:38

    浅谈Recycleview嵌套卡顿 以及你所不知道的解决方案

    由于项目需要,需要做到recycleview 这里先不讲如何用其他控件代替这种嵌套的方式 先假定你已经嵌套了 老板还不给时间给你给布局方式 那么这个时候你的这个嵌套列表 必定是卡顿的 只要超过一屏,展示效果会有卡顿现象,原因就是由于两个RecyclerView的存在,使得滑动的view滑出当前屏幕的释放存在冲突

  • 2019-05-21 12:35:54

    RecyclerView setHasFixedSize(true)的意义

    设置为true,再调用notifyDataSetChanged(),发现大小重新计算了,看来理解出现错误了。还是再看一下哪些地方用到这个mHasFixedSize吧。

  • 2019-05-21 12:37:34

    RecyclerView 刷新闪烁

    闪烁是 notifyDataSetChange 造成的。由于适配器不知道整个数据集中的哪些内容已经存在,在重新匹配 ViewHolder 时发生的。 当然,遇到这个问题时有去搜索一些答案,看到的很多都是去禁止 RecycleView 的默认动画,可惜这对我没什么用。下面的方法是对我有用的。