先来对比一下 :ListView 与 RecyclerView

ViewHolder

基本原理 :使不使用 ViewHolder 都在复用 convertView ,

区别,是 findViewById 的性能节省。

ListView 缓存机制

Android 优化页面记载速度 android recyclerview优化_Android 优化页面记载速度

Android 优化页面记载速度 android recyclerview优化_android_02

  • Active View

在 Active View 中的item ,在滑动过程中,

listView 自动帮我们复用了,不会再走getView()方法

凡是调用了 getView() 视图都需要重新绑定。

  • Scrap View

Scrap View 中的数据都是“脏”的,都需要走getView() 方法

RecyclerView 缓存机制

Android 优化页面记载速度 android recyclerview优化_数据_03

Android 优化页面记载速度 android recyclerview优化_数据_04

四层缓存
前两层,Scrap 和 Cache 数据都是干净的,都可以拿来直接用。
第三层,ViewCacheExtension 是用户自定义的缓存策略
第四层,RecycledViewPool 数据时‘脏’ ,不走 onCreateViewHolder,但是需要重新绑定会走 onBindViewHolder 方法

Android 优化页面记载速度 android recyclerview优化_android_05

Android 优化页面记载速度 android recyclerview优化_java_06

RecyclerView 可能不知道的 性能优化策略

1、不要将 view 的 setOnClickListener(new View.OnClickListener()) 写在 OnBindView中,这样会频繁创建,尽量写在 onCreateViewHolder 中(但是处理逻辑就会有些麻烦 if/else, switch等 )

2、LinearLayoutManager.setInitialPrefetchItemCount() 预加载的数量,避免一些交复杂的布局,突然滑入屏幕显示的时候出现问题()

横向列表初次显示时可见的 Item 个数

PS :只有 LinearLayoutManager 有这个 API;

只有嵌套在内部的 RecyclerView 才会生效

3、RecyclerView.setHasFixedSize(); (在recyclerView 数据变化 但是大小 不变的情况下,可以使用)

作用是:mHasFixedSize 为 true 时,回重新摆放 变化的 childLayout,不重新摆放所有布局(走 requestLayout方法)

void triggerUpdateProcessor() {
    if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
        ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
    } else {
        mAdapterUpdateDuringMeasure = true;
        requestLayout();
    }
}

4、RecycledViewPool 缓存池 (根据 viewType 进行缓存)

比如:下面多列表的情况,可能多个 Rv 使用了相同的 viewType , 那么他们就可以 共享 缓存池

看下源码注释

RecycledViewPool lets you share Views between multiple RecyclerViews.

If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.

RecyclerView automatically creates a pool for itself if you don’t provide one.

大概意思:

RecycledViewPool使您可以在多个RecyclerView之间共享视图。

如果要在RecyclerViews中回收视图,请创建RecycledViewPool实例,然后使用{@link RecyclerView#setRecycledViewPool(RecycledViewPool)}。

如果您不提供一个池,RecyclerView会自动为其自动创建一个池。

如何使用:
  • 开启这一项支持
需要注意的是,如果你使用的LayoutManager是LinearLayoutManager或其子类(如GridLayoutManager),需要手动开启这个特性:

layout.setRecycleChildrenOnDetach(true)

Android 优化页面记载速度 android recyclerview优化_Android 优化页面记载速度_07

Android 优化页面记载速度 android recyclerview优化_android_08

DiffUtil 增量更新(提升列表性能)
DiffUtil .Callback (这是一个,给系统看的 callback,用于计算 diff 的 )
一共有5个方法:

Android 优化页面记载速度 android recyclerview优化_缓存_09

Android 优化页面记载速度 android recyclerview优化_java_10

下面 3 个方法 的返回值关系,以及执行顺序是

Android 优化页面记载速度 android recyclerview优化_缓存_11

Android 优化页面记载速度 android recyclerview优化_android_12

可以不实现 getChangePayload 方法,但是这样就不能看到RV 的增量更新了

Android 优化页面记载速度 android recyclerview优化_数据_13

如何 在adapter 中使用 :

Android 优化页面记载速度 android recyclerview优化_Android 优化页面记载速度_14

这时候 发现 getChangePayload 中 返回的 payload 并没有被使用,在 adapter 中有一个 带 payloads 参数的 onBindViewHolder 重载方法, 他做的事就是 局部绑定

@Override
    public void onBindViewHolder(@NonNull DemoViewHolder holder, int position, @NonNull List<Object> payloads) {
        super.onBindViewHolder(holder, position, payloads);
        
    }

Android 优化页面记载速度 android recyclerview优化_数据_15

DiffUtil 的效率
在列表很大的时候计算 Diff ,计算可能会导致掉帧(>60ms)。
google 的推荐方法是 在列表很大的时候 【异步计算】 Diff

Android 优化页面记载速度 android recyclerview优化_缓存_16

为什么 ItemDecoration 可以绘制分隔线?
ItemDecoration 还能做什么事情?
  • 分割线

Android 优化页面记载速度 android recyclerview优化_java_17

  • 将item 进行分组

Android 优化页面记载速度 android recyclerview优化_java_18