【Android Framework系列】第12章 RecycleView相关原理及四级缓存策略分析

1 RecyclerView简介

RecyclerView是一款非常强大的widget,它可以帮助您灵活地显示列表数据。当我开始学习 RecyclerView的时候,我发现对于复杂的列表界面有很多资源可以参考,但是对于简单的列表展现就鲜有可参考的资源了。虽然RecyclerView的组成结构乍一看有些复杂,但是深入理解以后您会发现它其实非常简单明了。
RecyclerView一般作为Android显示列表的控件,有诸多优异的性能。回收池策略能加载上亿级数据并不发生卡顿,适配器模式能展示任意显示需求。
RecyclerView就像传送带,充分利用传送带原理,永远只有用户看到的数据才会加载到内存,而看不到的在等待被加载。传送带能够源源不断的传送亿级货物,RecyclerView也能够显示加载亿级Item

1.3 RecyclerView架构中核心组件

  1. 回收池:能回收任意Item控件,并返回符合类型的Item控件;
    比如 onBinderViewHodler方法中的第一个参数是从回收池中返回的
  2. 适配器:Adapter接口,经常辅助RecyclerView实现列表展示;适配器模式,将用户界面展示与交互分离
  3. RecyclerView:是做触摸事件的交互,主要实现边界值判断;根据用户的触摸反馈,协调回收池对象与适配器对象之间的工作

我们带着这几个问题来学习RecyclerView

  1. ListviewRecycerview的缓存差别
  2. RecyclerView滑出去的View到哪里去了
  3. RecyclerView如何复用回收池的View
  4. RecyclerView的四级缓存机制

1.4 RecyclerView滑动相关

众所周知,RecyclerViewandroid中实现列表是性能非常好的,那么性能好的原因在哪里呢?关键还是在它在处理view时的回收和复用。列表在滑动的时候,会进行itemView的回收和复用,那么我们就从滑动回调即onTouchEvent来入手分析

1.4.1 基本概念

  1. ViewHolder: View的容器,一项View就对应一个ViewHolder
  2. Recyler:RecyclerView的内部类,主要负责View的回收和复用
  3. LinearLayoutManager: RecyclerView的线性布局管理器

1.4.2 滑动时函数调用链

在这里插入图片描述
这里我们大概了解下滑动时的函数调用链,帮助理解后面分析四级缓存相关的思路。

1.4.3 onMeasrue初始化

RecyclerView的宽度和高度开发者们都喜欢设置层wrap_content或者match_parent。所以需要通过实际内容确定RecyclerView高度
情况1 : 当item数不足的时候,比如RecyclerView只加载了2个Item 以子控件总高度测算的高度为准
情况2 : 当item数量超过实际屏幕高度,以match_parent为准,也就是最大高度

1.4.4 onLayout

RecyclerView作为一个容器类控件 继承自ViewGroup。必须实现onLayout方法来子控件进行正确摆放,由于我们手写的RecyclerVIew是垂直的,摆放是由上至下进行。同时为了不将所有Item全部加载到内存 也需要进行准确的控制

1.4.5 事件拦截

RecyclerView作为一个容器类控件 需要拦截滑动事件,用户手指滑动则让所有子Item滑动,子Item在滑动中是接收不到任何事件的。当RecyclerVIew静止时,子Item需要接收到点击事件

2 RecyclerView适配器与回收池的工作机制

这里我们先以图片的形式了解一下RecyclerView相关的加载逻辑。

2.1 RecyclerView中的第一屏加载

在这里插入图片描述

2.2 RecyclerView中的第二屏

在这里插入图片描述

2.3 回收池回收策略

在这里插入图片描述

2.4 回收池填充策略

在这里插入图片描述

2.5 回收池设计

在这里插入图片描述

3 RecyclerView回收与复用

在这里插入图片描述

3.1 回收的关键方法分析

RecyclerView.java

void recycleViewHolderInternal(ViewHolder holder) {
    
    
    ...
    if (forceRecycle || holder.isRecyclable()) {
    
    
                if (mViewCacheMax > 0
                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                                | ViewHolder.FLAG_REMOVED
                                | ViewHolder.FLAG_UPDATE
                                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) 
    // 1) 先尝试放到cacheView中
                    int cachedViewSize = mCachedViews.size();
                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
    
    
                        // 如果 mCachedViews 已经满了,把第0个位置的移除并放到 缓存池中
                        recycleCachedViewAt(0);
                        cachedViewSize--;
                    }
                    if (!cached) {
    
    
                    // 2) 如果CacheView中没放进去,就放到 缓存池中
                    addViewHolderToRecycledViewPool(holder, true);
                    recycled = true;
                }
           ...
}

3.2 复用的关键方法分析

tryGetViewHolderForPositionByDeadline
从一级缓存 mChangeScrap 中取
从二级缓存 mCachedViews中取
从三级缓存 mViewCacheExtension 中取
从四级缓存 缓存池中取
缓存中都没有拿到值,就直接创建
未绑定过时,进行绑定

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
    
    
    ViewHolder holder = null;
            // 1) 从一级缓存 changed scrap 中取
            if (mState.isPreLayout()) {
    
    
                holder = getChangedScrapViewForPosition(position);
                fromScrapOrHiddenOrCache = holder != null;
            }
            // 2)从二级缓存 cache中取
            if (holder == null) {
    
    
                final int type = mAdapter.getItemViewType(offsetPosition);
                if (mAdapter.hasStableIds()) {
    
    
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
                    if (holder != null) {
    
    
                        // update position
                        holder.mPosition = offsetPosition;
                        fromScrapOrHiddenOrCache = true;
                    }
                }
                // 3. 从三级缓存 CacheExtension 中取  
                if (holder == null && mViewCacheExtension != null) {
    
    
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if (view != null) {
    
    
                        holder = getChildViewHolder(view);
                    }
                }
                // 4) 从四级缓存 缓存池中取
                if (holder == null) {
    
     // fallback to pool
                    holder = getRecycledViewPool().getRecycledView(type);
                    if (holder != null) {
    
    
                        holder.resetInternal();
                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
    
    
                            invalidateDisplayListInt(holder);
                        }
                    }
                }
                // 5)缓存中都没有拿到值,就直接创建
                if (holder == null) {
    
    
                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
                    if (ALLOW_THREAD_GAP_WORK) {
    
    
                        // only bother finding nested RV if prefetching
                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
                        if (innerView != null) {
    
    
                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
                        }
                    }

                    long end = getNanoTime();
                    mRecyclerPool.factorInCreateTime(type, end - start);
                    if (DEBUG) {
    
    
                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
                    }
                }
            }
            // 6)已经 bind过了,不会再去绑定,未绑定过时,进行绑定
            if (mState.isPreLayout() && holder.isBound()) {
    
    
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
    
    
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                //  尝试 bindView
                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
            }
            return holder;
}

4 四级缓存机制

4.1 一级缓存-缓存碎片

ViewHolder getChangedScrapViewForPosition(int position) {
    
    
            // If pre-layout, check the changed scrap for an exact match.
            final int changedScrapSize;
            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
    
    
                return null;
            }
            // find by position
            for (int i = 0; i < changedScrapSize; i++) {
    
    
                final ViewHolder holder = mChangedScrap.get(i);
                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
    
    
                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                    return holder;
                }
            }
            // find by id
            if (mAdapter.hasStableIds()) {
    
    
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
    
    
                    final long id = mAdapter.getItemId(offsetPosition);
                    for (int i = 0; i < changedScrapSize; i++) {
    
    
                        final ViewHolder holder = mChangedScrap.get(i);
                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
    
    
                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                            return holder;
                        }
                    }
                }
            }
            return null;
}

4.2 二级缓存-缓存列表

ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
    
    
            // 1) 先从mAttachedScrap中取,取到便返回
            final int count = mAttachedScrap.size();
            for (int i = count - 1; i >= 0; i--) {
    
    
                final ViewHolder holder = mAttachedScrap.get(i);
                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
    
    
                    if (type == holder.getItemViewType()) {
    
    
                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                        if (holder.isRemoved()) {
    
    
                            if (!mState.isPreLayout()) {
    
    
                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
                            }
                        }
                        return holder;
                    } else if (!dryRun) {
    
    
                        mAttachedScrap.remove(i);
                        removeDetachedView(holder.itemView, false);
                        quickRecycleScrapView(holder.itemView);
                    }
                }
            }

            // 2)二级缓存,从mCachedViews中取
            final int cacheSize = mCachedViews.size();
            for (int i = cacheSize - 1; i >= 0; i--) {
    
    
                final ViewHolder holder = mCachedViews.get(i);
                //从mCachedViews中取到后便返回
                if (holder.getItemId() == id) {
    
    
                    if (type == holder.getItemViewType()) {
    
    
                        if (!dryRun) {
    
    
                            mCachedViews.remove(i);
                        }
                        return holder;
                    } else if (!dryRun) {
    
    
                        recycleCachedViewAt(i);
                        return null;
                    }
                }
            }
            return null;
        }

4.3 三级缓存-自定义缓存

这一级缓存为用户自定义,这里不做详解。

4.4 四级缓存-缓存池

public ViewHolder getRecycledView(int viewType) {
    
    
            //从mScrap中根据view的类型来取出一个
            final ScrapData scrapData = mScrap.get(viewType);
            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
    
    
                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
                //从 scrapData 中拿最后一个数据,先进后出
                return scrapHeap.remove(scrapHeap.size() - 1);
            }
            return null;
}

static class ScrapData {
    
    
            //ViewHolder是作为一个List被存进来的
            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
            // 缓存池中 list的大小是5,也就是每个类型的view缓存池中存储5个
            int mMaxScrap = 5;
            long mCreateRunningAverageNs = 0;
            long mBindRunningAverageNs = 0;
 }

猜你喜欢

转载自blog.csdn.net/u010687761/article/details/132525589
今日推荐