当前位置: 首页 > news >正文

Android面试总结之Android RecyclerView:从基础机制到缓存优化

引言

在 Android 开发中,RecyclerView是高效展示列表数据的核心组件。其强大的性能源于独特的视图复用机制和四级缓存体系。本文将结合源码与示例,带你深入理解RecyclerView的工作原理与优化策略。

核心组件

  • RecyclerView:作为容器视图,负责管理和展示列表数据。它会根据布局管理器对列表项进行布局。
  • LayoutManager:用于确定列表项的布局方式,如线性布局、网格布局、瀑布流布局等。
  • Adapter:充当数据和视图之间的桥梁,负责将数据绑定到视图上。它会创建列表项的视图并填充数据。
  • ViewHolder:用来缓存列表项视图中的子视图,避免每次都通过 findViewById 查找视图,从而提升性能。

工作流程

1. 初始化 RecyclerView

在布局文件里添加 RecyclerView 组件,然后在代码中对其进行初始化。

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        // 设置布局管理器
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
}
2. 创建 Adapter 和 ViewHolder

Adapter 要继承 RecyclerView.Adapter,并实现必要的方法。ViewHolder 则要继承 RecyclerView.ViewHolder

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private List<String> dataList;

    public MyAdapter(List<String> dataList) {
        this.dataList = dataList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // 创建视图
        View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        // 绑定数据
        String data = dataList.get(position);
        holder.textView.setText(data);
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    public static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(android.R.id.text1);
        }
    }
}
3. 设置 Adapter

在 Activity 或者 Fragment 中为 RecyclerView 设置 Adapter

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        // 模拟数据
        List<String> dataList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            dataList.add("Item " + i);
        }

        // 设置 Adapter
        MyAdapter adapter = new MyAdapter(dataList);
        recyclerView.setAdapter(adapter);
    }
}

视图复用机制

RecyclerView 最核心的优化点在于视图复用机制。当列表项滑出屏幕时,RecyclerView 不会将其销毁,而是把它放入回收池中。当新的列表项需要显示时,RecyclerView 会优先从回收池中获取可用的视图,然后通过 Adapter 的 onBindViewHolder 方法为其绑定新的数据,这样就避免了频繁创建和销毁视图,大大提升了性能。

RecyclerView 的缓存机制是其高效展示大量数据的关键所在,它能够显著减少视图创建和销毁的开销,从而提升性能和响应速度。下面为你详细介绍 RecyclerView 的缓存机制。

缓存层级

RecyclerView 的缓存机制包含四级缓存,分别是:

  1. Scrap 缓存(mAttachedScrap 和 mChangedScrap)
    • 功能:这是 RecyclerView 的一级缓存,用于临时存储正在被重新布局的 ViewHolder。当 RecyclerView 进行布局操作时,比如滚动、插入或删除项目,这些 ViewHolder 会被暂时从屏幕上移除并放入 Scrap 缓存中,布局完成后再从这里取出重新使用。
    • 特点:速度最快,因为这些 ViewHolder 无需重新绑定数据,可直接复用。
  2. Cache 缓存(mCachedViews)
    • 功能:二级缓存,用于存储最近被移除屏幕的 ViewHolder。当用户快速滚动列表时,这些 ViewHolder 可以被快速复用,而不需要重新创建和绑定数据。
    • 特点:默认大小为 2,可以通过 setItemViewCacheSize 方法进行调整。缓存中的 ViewHolder 保持着原有的数据和状态,复用速度较快。
  3. ViewCacheExtension 缓存
    • 功能:三级缓存,这是一个由开发者自定义的缓存接口。开发者可以根据自身需求实现这个接口,以创建自定义的缓存逻辑。
    • 特点:灵活性高,但需要开发者自己管理缓存的添加、移除和查找操作。
  4. RecycledViewPool 缓存
    • 功能:四级缓存,用于存储不同类型的 ViewHolder。当 Cache 缓存已满时,新移除的 ViewHolder 会被放入 RecycledViewPool 中。当需要新的 ViewHolder 时,如果 Scrap 缓存和 Cache 缓存中没有可用的,就会从 RecycledViewPool 中查找。
    • 特点:这里的 ViewHolder 会被重置,需要重新绑定数据。不同类型的 ViewHolder 可以有不同的缓存大小,默认每个类型的缓存大小为 5。

缓存工作流程

1. 获取 ViewHolder

当 RecyclerView 需要一个新的 ViewHolder 来显示列表项时,会按照以下顺序从缓存中查找:

  • 首先检查 Scrap 缓存,如果找到匹配的 ViewHolder,直接使用,无需重新绑定数据。
  • 若 Scrap 缓存中没有,接着检查 Cache 缓存。如果找到,同样可以直接使用,并且保留原有的数据和状态。
  • 若 Cache 缓存也没有,再检查自定义的 ViewCacheExtension 缓存。
  • 若以上缓存都没有找到,最后检查 RecycledViewPool 缓存。如果找到,需要重新绑定数据。
  • 如果所有缓存中都没有找到合适的 ViewHolder,则调用 Adapter 的 onCreateViewHolder 方法创建一个新的 ViewHolder,并绑定数据。
2. 回收 ViewHolder

当列表项滑出屏幕时,ViewHolder 会按照以下规则被回收:

  • 首先尝试将 ViewHolder 放入 Cache 缓存中。如果 Cache 缓存已满,则将最旧的 ViewHolder 移除,放入 RecycledViewPool 中,然后将新的 ViewHolder 放入 Cache 缓存。
  • 如果 Cache 缓存没有满,直接将 ViewHolder 放入 Cache 缓存。

示例代码

以下是一个简单的示例,展示了如何使用 RecycledViewPool

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView1, recyclerView2;
    private RecyclerView.RecycledViewPool viewPool;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化 RecycledViewPool
        viewPool = new RecyclerView.RecycledViewPool();

        // 第一个 RecyclerView
        recyclerView1 = findViewById(R.id.recyclerView1);
        recyclerView1.setLayoutManager(new LinearLayoutManager(this));
        recyclerView1.setRecycledViewPool(viewPool);
        List<String> dataList1 = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            dataList1.add("Item 1 - " + i);
        }
        MyAdapter adapter1 = new MyAdapter(dataList1);
        recyclerView1.setAdapter(adapter1);

        // 第二个 RecyclerView
        recyclerView2 = findViewById(R.id.recyclerView2);
        recyclerView2.setLayoutManager(new LinearLayoutManager(this));
        recyclerView2.setRecycledViewPool(viewPool);
        List<String> dataList2 = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            dataList2.add("Item 2 - " + i);
        }
        MyAdapter adapter2 = new MyAdapter(dataList2);
        recyclerView2.setAdapter(adapter2);
    }
}

在这个示例中,两个 RecyclerView 共享同一个 RecycledViewPool,这样可以提高视图的复用率,减少内存开销。

缓存层级对比

缓存层级存储内容特点典型场景
Scrap 缓存当前屏幕可见 ViewHolder无需重新绑定数据布局更新时复用
Cache 缓存最近移出屏幕的 ViewHolder保留数据状态快速滚动时复用
ViewCacheExtension自定义缓存逻辑开发者可控特殊业务场景
RecycledViewPool未绑定数据的 ViewHolder跨列表共享缓存多个列表复用同一 ViewPool

2. 缓存工作流程

  1. 获取 ViewHolder:按层级依次查找 Scrap → Cache → ViewCacheExtension → RecycledViewPool → 新建
  2. 回收 ViewHolder:移出屏幕时优先存入 Cache,满容后转移至 RecycledViewPool

3. 缓存优化实践

// 配置缓存大小
recyclerView.setItemViewCacheSize(5); // 增大Cache缓存容量
recyclerView.getRecycledViewPool()
    .setMaxRecycledViews(ViewType, 10); // 调整RecycledViewPool容量

// 跨列表共享缓存
RecyclerView recyclerView1 = findViewById(R.id.rv1);
RecyclerView recyclerView2 = findViewById(R.id.rv2);
recyclerView2.setRecycledViewPool(recyclerView1.getRecycledViewPool());

性能优化建议

  1. 合理使用 ViewHolder:避免在onBindViewHolder中执行耗时操作
  2. 数据变化优化:使用DiffUtil实现局部刷新
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyCallback(oldList, newList));
diffResult.dispatchUpdatesTo(adapter);
  1. 布局优化:减少布局层级,使用merge标签
  2. 预加载策略:通过addOnScrollListener实现分页加载

总结

RecyclerView通过视图复用四级缓存实现了高效的列表渲染,其设计思想对理解现代 UI 框架具有重要参考价值。在实际开发中,需根据业务场景合理配置缓存参数,结合DiffUtil等工具实现性能与体验的平衡。

感谢观看!!!

相关文章:

  • 浅尝AI编程工具Trae
  • javascript实现一个函数,将数组中的元素随机打乱顺序
  • 如何用C#继承提升游戏开发效率?Enemy与Boss案例解析
  • 什么是ecovadis认证?ecovadis认证的好处?ecovadis认证的重要意义
  • 案例4:鸢尾花分类(pytorch)
  • 【Docker系列八】使用 Docker run 命令部署 Nginx
  • 初识哈希表
  • 详解接口的常见请求方式
  • 机器学习(八)
  • 1342 摆放小球
  • uniapp中props的用法
  • 3.24学习总结 Java多态+包和final关键字
  • 大文件切片上传和断点续传
  • Typora1.10破解教程
  • 数智读书笔记系列024《主数据驱动的数据治理 —— 原理、技术与实践》
  • SSM整合
  • MySQL MVCC的快照读和当前读区别,Redis的RDB+AOF混合持久化流程。
  • 用SVG绕过浏览器XSS审计
  • Axure项目实战:智慧城市APP(五)新闻资讯、就业信息(动态面板)
  • 微调0.5 B-32B模型要达到85%的准确率需要的数据和资源-会话质检和会话小结
  • 长三角体育节回归“上海时间”,首次发布赛事旅游推荐线路
  • 浙江演艺集团7部作品组团来沪,今夏开启首届上海演出季
  • 美国关税压力下,日本经济一年来首次萎缩
  • 农行回应“病重老人被要求亲自取钱在银行去世”:全力配合公安机关调查
  • 男子恶意遗弃幼子获刑,最高法发布涉未成年人家庭保护典型案例
  • 消息人士称泽连斯基已启程前往土耳其