Android应用开发那些事

RecyclerView 简析之缓存机制及优化

2020-05-14  本文已影响0人  tandeneck

RecyclerView 是用于大量数据展示的控件,相对于传统的 ListView ,更加强大和灵活。

缓存机制

RecyclerView 与 ListView 的缓存机制原理大致相似, 滑动的时候,离屏的 ItemView 被回收至缓存,入屏的 ItemView 则会优先从缓存中获取,只是 ListView 与 RecyclerView 的实现细节有差异。

ListView 缓存机制

ListView 主要是二级缓存,缓存的对象是 View,ListView 是继承于 AbsListView 的,而 AbsListView 里面有个 mRecycler,用于存储不使用的 view,其将被下次 layout 的时候重新使用,以避免创建新的实例。

    /**
     * The data set used to store unused views that should be reused during the next layout
     * to avoid creating new ones
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769398)
    final RecycleBin mRecycler = new RecycleBin();

RecycleBin 是 AbsListView 的内部类,其作用是通过两级缓存来缓存 view。(RecycleBin 在 layout 的过程中便于 view 重用,RecycleBin 有两级缓存:mActiveViews 和 mScrapViews)。

RecyclerView 缓存机制

同样地,RecyclerView 也有一个类专门来管理缓存,不过与 ListView 不同的是,RecylerView 缓存的是 ViewHolder,而且实现的是四级缓存,如下:

public final class Recycler {
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
private ArrayList<ViewHolder> mChangedScrap = null;

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

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

private RecycledViewPool mRecyclerPool;

private ViewCacheExtension mViewCacheExtension;
public static class RecycledViewPool {

 // 根据 viewType 保存的被废弃的 ViewHolder 集合,以便下次使用
 private SparseArray<ArrayList<ViewHolder>> mScrap = new SparseArray<ArrayList<ViewHolder>>();
  /**
   * 从缓存池移除并返回一个 ViewHolder
   */
  public ViewHolder getRecycledView(int viewType) {
    final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
    if (scrapHeap != null && !scrapHeap.isEmpty()) {
      final int index = scrapHeap.size() - 1;
      final ViewHolder scrap = scrapHeap.get(index);
      scrapHeap.remove(index);
      return scrap;
    }
      return null;
    }

  public void putRecycledView(ViewHolder scrap) {
    final int viewType = scrap.getItemViewType();
    final ArrayList scrapHeap = getScrapHeapForType(viewType);
    if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
      return;
    }
    scrap.resetInternal();
    scrapHeap.add(scrap);
  }

  /**
   * 根据 viewType 获取对应缓存池
   */
  private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
    ArrayList<ViewHolder> scrap = mScrap.get(viewType);
      if (scrap == null) {
        scrap = new ArrayList<>();
        mScrap.put(viewType, scrap);
          if (mMaxScrap.indexOfKey(viewType) < 0) {
            mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
          }
      }
    return scrap;
  }
}

顾名思义,它是一个缓存池,实现上,是通过一个默认为 5 大小的 ArrayList 实现的。这一点,同 ListView 的 RecyclerBin 这个类一样。每一个 ArrayList 又都是放在一个 Map 里面的,SparseArray 用两个数组用来替代 Map。
把所有的 ArrayList 放在一个 Map 里面,这也是 RecyclerView 最大的亮点,这样根据 itemType 来取不同的缓存 Holder,每一个 Holder 都有对应的缓存,而只需要为这些不同 RecyclerView 设置同一个 Pool 就可以了。
这个可以在 Pool 的 setRecycledViewPool() 方法可以看到注释:

/**
 * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
 * This can be useful if you have multiple RecyclerViews with adapters that use the same
 * view types, for example if you have several data sets with the same kinds of item views
 * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
 *
 * @param pool Pool to set. If this parameter is null a new pool will be created and used.
 */
public void setRecycledViewPool(RecycledViewPool pool) {
    mRecycler.setRecycledViewPool(pool);
}

RecyclerView 优化

mTextView.setText(Html.fromHtml(data).toString());

这里的 Html.fromHtml(data) 方法可能就是比较耗时的,存在多个 TextView 的话耗时会更为严重,这样便会引发掉帧、卡顿,故此时应该在子线程处理。

new LinearLayoutManager(this) {
    @Override
    protected int getExtraLayoutSpace(RecyclerView.State state) {
        return size;
    }
};
上一篇下一篇

猜你喜欢

热点阅读