RecyclerView源码分析—缓存原理

概要

RecyclerView是Android开发中一个至关重要的UI控件,在日常项目的业务开发中无处不在,功能也极其强大。子View不同逻辑解耦,view回收复用高性能,易用性体现在局部刷新、item动画,拖拽测滑等,基本能替代ListView所有功能(但也并不能完全替代ListView,ListView并没有被标记为@Deprecated,关于替换的必要性可以参考【腾讯Bugly干货分享】Android ListView与RecyclerView对比浅析–缓存机制)。RecyclerView核心优势是缓存机制的设计,本文以RecyclerView缓存原理为主线,部分源码进行分析,从RecyclerView的缓存结构,缓存管理以及缓存使用等方面进行展开。

RecylerView缓存的简单梳理,RecylerView中一共有五种缓存,分别是:

  • mScrapView
  • mAttachedScrap
  • mCachedViews
  • mViewCacheExtension
  • mRecyclerPool

其中前两种mScrapView、mAttachedScrap并不对外暴露,真正开发中能控制或自定义的是后三种mCachedViews、mViewCacheExtension和mRecyclerPool,所以在学习RecyclerView缓存原理的过程中,建议的方向是:理解前两种的作用以及相关源码,理解后三者的作用、源码并掌握实际用法。在阅读理解过程中结合实践对关键方法和变量进行跟踪debug,会更快的掌握整个知识体系。

注:本文引用的RecyclerView相关源码为最新api 29(Android Q),recyclerView-v7版本29.0.0(即最新sdk版本29.0.0)包下的源代码,查看最新源码需要最新测试版编译器Android Studio 3.5 Beta 4,具体在as配置文件中引用为:

dependencies {
   
    ...
    implementation 'com.android.support:recyclerview-v7:29.0.0'
    ...  
}

RecyclerView缓存结构

RecyclerView缓存本质上指的ViewHolder缓存,下面是源码中五种缓存变量的数据结构:

  • mChangedScrap
ArrayList<ViewHolder> mChangedScrap = null;
  • mAttachedScrap
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
  • mCachedViews
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
  • mViewCacheExtension

ViewCacheExtension是一个abstranct类,暴露给应用层实现,只有一个abstract的getViewForPositionAndType方法需要覆写。

private ViewCacheExtension mViewCacheExtension;
 
 /**
   * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
   * be controlled by the developer.
   * <p>
   * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
   * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
   * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
   * {@link RecycledViewPool}.
   * <p>
   * Note that, Recycler never sends Views to this method to be cached. It is developers
   * responsibility to decide whether they want to keep their Views in this custom cache or let
   * the default recycling policy handle it.
   */
  public abstract static class ViewCacheExtension {
   

      /**
       * Returns a View that can be binded to the given Adapter position.
       * <p>
       * This method should <b>not</b> create a new View. Instead, it is expected to return
       * an already created View that can be re-used for the given type and position.
       * If the View is marked as ignored, it should first call
       * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
       * <p>
       * RecyclerView will re-bind the returned View to the position if necessary.
       *
       * @param recycler The Recycler that can be used to bind the View
       * @param position The adapter position
       * @param type     The type of the View, defined by adapter
       * @return A View that is bound to the given position or NULL if there is no View to re-use
       * @see LayoutManager#ignoreView(View)
       */
      @Nullable
      public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
                int type);
  }
  • mRecyclerPool
/**
  * RecycledViewPool lets you share Views between multiple RecyclerViews.
  * <p>
  * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
  * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
  * <p>
  * RecyclerView automatically creates a pool for itself if you don't provide one.
  */
 public static class RecycledViewPool {
   
     private static final int DEFAULT_MAX_SCRAP = 5;

     /**
      * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
      *
      * Note that this tracks running averages of create/bind time across all RecyclerViews
      * (and, indirectly, Adapters) that use this pool.
      *
      * 1) This enables us to track average create and bind times across multiple adapters. Even
      * though create (and especially bind) may behave differently for different Adapter
      * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
      *
      * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
      * false for all other views of its type for the same deadline. This prevents items
      * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
      */
     static class ScrapData {
   
         final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
         int mMaxScrap = DEFAULT_MAX_SCRAP;
         long mCreateRunningAverageNs = 0;
         long mBindRunningAverageNs = 0;
     }
     SparseArray<ScrapData> mScrap = new SparseArray<>();
        
    ...
       
     /**
      * Sets the maximum number of ViewHolders to hold in the pool before discarding.
      *
      * @param viewType ViewHolder Type
      * @param max Maximum number
      */
     public void setMaxRecycledViews(int viewType, int max) {
   
         ScrapData scrapData = getScrapDataForType(viewType);
         scrapData.mMaxScrap = max;
         final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
         while (scrapHeap.size() > max) {
   
             scrapHeap.remove(scrapHeap.size() - 1);
         }
     }

     /**
      * Returns the current number of Views held by the RecycledViewPool of the given view type.
      */
     public int getRecycledViewCount(int viewType) {
   
         return getScrapDataForType(viewType).mScrapHeap.size();
     }

     /**
      * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
      * present.
      *
      * @param viewType ViewHolder type.
      * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
      * are present.
      */
     @Nullable
     public ViewHolder getRecycledView(int viewType) {
   
         final ScrapData scrapData = mScrap.get(viewType);
         if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
   
             final ArrayList
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值