Android面試總結之Android RecyclerView:從基礎機制到緩存優化

引言

在 Android 開發中,RecyclerView是高效展示列表數據的核心組件。其強大的性能源于獨特的視圖復用機制和四級緩存體系。本文將結合源碼與示例,帶你深入理解RecyclerView的工作原理與優化策略。

核心組件

  • RecyclerView:作為容器視圖,負責管理和展示列表數據。它會根據布局管理器對列表項進行布局。
  • LayoutManager:用于確定列表項的布局方式,如線性布局、網格布局、瀑布流布局等。
  • Adapter:充當數據和視圖之間的橋梁,負責將數據綁定到視圖上。它會創建列表項的視圖并填充數據。
  • ViewHolder:用來緩存列表項視圖中的子視圖,避免每次都通過?findViewById?查找視圖,從而提升性能。

工作流程

1. 初始化?RecyclerView

在布局文件里添加?RecyclerView?組件,然后在代碼中對其進行初始化。

<androidx.recyclerview.widget.RecyclerViewandroid: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;@Overrideprotected 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@Overridepublic 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);}@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {// 綁定數據String data = dataList.get(position);holder.textView.setText(data);}@Overridepublic 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;@Overrideprotected 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);}// 設置 AdapterMyAdapter 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 RecycledViewPoolviewPool = new RecyclerView.RecycledViewPool();// 第一個 RecyclerViewrecyclerView1 = 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);// 第二個 RecyclerViewrecyclerView2 = 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等工具實現性能與體驗的平衡。

感謝觀看!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899056.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899056.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899056.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【鴻蒙開發】Hi3861學習筆記- TCP客戶端

00. 目錄 文章目錄 00. 目錄01. TCP概述02. TCP應用場景03. TCP和UDP比較04. TCP相關API05. TCP編程流程06. 硬件設計07. 軟件設計08. 實驗現象09. 附錄 01. TCP概述 TCP&#xff08;Transmission Control Protocol&#xff09;是一種面向連接、可靠的傳輸層協議&#xff0c;旨…

【負載均衡系列】Keepalive

一、Keepalived 的核心功能 Keepalived 是一款用于實現 ?高可用(HA)? 和 ?負載均衡 的開源工具,核心基于 ?VRRP(Virtual Router Redundancy Protocol)? 協議,工作在網絡四層(傳輸層)和七層(應用層)。 主要用途: 通過虛擬IP(VIP)實現服務高可用(主備切換)。…

2025-03-25 學習記錄--C/C++-PTA 習題9-3 平面向量加法

合抱之木&#xff0c;生于毫末&#xff1b;九層之臺&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、題目描述 ?? 習題9-3 平面向量加法 本題要求編寫程序&#xff0c;計算兩個二維平面向量的和向量。 輸入格式: ? 輸入在…

23種設計模式-橋接(Bridge)設計模式

橋接設計模式 &#x1f6a9;什么是橋接設計模式&#xff1f;&#x1f6a9;橋接設計模式的特點&#x1f6a9;橋接設計模式的結構&#x1f6a9;橋接設計模式的優缺點&#x1f6a9;橋接設計模式的Java實現&#x1f6a9;代碼總結&#x1f6a9;總結 &#x1f6a9;什么是橋接設計模式…

python:music21 構建 LSTM+GAN 模型生成爵士風格音樂

keras_lstm_gan_midi.py 這是一個結合 LSTM 和 GAN 生成爵士風格音樂的完整Python腳本。這個實現包含音樂特征提取、對抗訓練機制和MIDI生成功能&#xff1a; import numpy as np from music21 import converter, instrument, note, chord, stream from tensorflow.keras.mode…

go:前后端分離

1.前端代碼 新建一個前端文件夾&#xff0c;在該文件夾下新建一個.html文件&#xff0c;寫入自己的html代碼。 前端搞定。 2.后端代碼 其核心是掛載路由接受前端傳來的數據核心代碼如下&#xff1a; func main() { // 服務運行提示 fmt.Println("go web server is runn…

大數據學習(86)-Zookeeper去中心化調度

&#x1f34b;&#x1f34b;大數據學習&#x1f34b;&#x1f34b; &#x1f525;系列專欄&#xff1a; &#x1f451;哲學語錄: 用力所能及&#xff0c;改變世界。 &#x1f496;如果覺得博主的文章還不錯的話&#xff0c;請點贊&#x1f44d;收藏??留言&#x1f4dd;支持一…

JetsonNano —— 4、Windows下對JetsonNano板卡燒錄刷機Ubuntu20.04版本(官方教程)

介紹 NVIDIA Jetson Nano? 開發者套件是一款面向創客、學習者和開發人員的小型 AI 計算機。按照這個簡短的指南&#xff0c;你就可以開始構建實用的 AI 應用程序、酷炫的 AI 機器人等了。 燒錄刷機 1、下載 Jetson Nano開發者套件SD卡映像 解壓出.img文件并記下它在計算機上的…

HTML5 拖放(Drag and Drop)學習筆記

一、HTML5 拖放簡介 HTML5 拖放&#xff08;Drag and Drop&#xff09;是HTML5標準的一部分&#xff0c;允許用戶抓取一個對象并將其拖動到另一個位置。拖放功能在現代網頁中非常常見&#xff0c;例如文件上傳、任務管理、布局調整等場景。 HTML5 拖放功能支持以下瀏覽器&…

文件I/O--C++的文件操作

一、打開文件&#xff0c;從文件中讀取、寫入文件 從文件中讀取數據&#xff1a; #include<fstream> //fstream File stream:文件流 #include<iostream> //fstream包含了 iostream&#xff0c;所以這句可以省略&#xff0c;現在不能了 using namespace std;i…

Redis GEO 命令詳解:輕松實現“附近的人“功能

目錄 引言 Redis GEO命令概述 什么是GEO命令&#xff1f; 主要命令詳解 命令應用示例 添加地點信息 查詢兩地距離 查詢附近的城市 實現"查找附近的人"功能 功能需求與實現思路 基本需求 實現思路 命令實現方案 存儲用戶位置 查詢附近的用戶 Java代碼實…

C語言貪吃蛇實現

When the night gets dark,remember that the Sun is also a star. 當夜幕降臨時&#xff0c;請記住太陽也是一顆星星。 ————《去月球海灘篇》 目錄 文章目錄 一、《貪吃蛇》游戲介紹 二、WIN32部分接口簡單介紹 2.1 控制臺窗口大小設置 2.2 命令行窗口的名稱的變更 2…

NIO入門

IO和NIO的區別&#xff1a; IO&#xff1a;通過流處理數據&#xff0c;僅支持阻塞IO。 核心組件&#xff1a;InputStream /OutputStream用于字節的讀寫&#xff0c;Reader / Writer&#xff1a;用于字符流的讀寫。讀取過程中無法被中斷&#xff0c;是阻塞式IO。 NIO:通過管道處…

基于vue.js開發的家庭裝修管理系統開發與設計(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 本家庭裝修管理系統采用B/S架構&#xff0c;數據庫是MySQL&#xff0c;網站的搭建與開發采用了先進的Node.js語言進行編寫&#xff0c;使用了VUE框架。該系統從兩個對象&#xff1a;由管理員和用戶來對系統進行設計構建。用戶的功能包括&#xff1a;注冊、登錄、瀏覽首頁…

OpenCV圖像拼接(5)圖像拼接模塊的用于創建權重圖函數createWeightMap()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::detail::createWeightMap 是 OpenCV 庫中用于圖像拼接模塊的一個函數&#xff0c;主要用于創建權重圖。這個權重圖在圖像拼接過程中扮演著重…

LangGraph 怎么控制遞歸次數

這一節我們講講langgraph圖的遞歸限制 Recursion Limit&#xff0c;遞歸限制設置了圖在單次執行過程中可以執行的最大超級步驟數。一旦達到該限制&#xff0c;LangGraph 將引發 GraphRecursionError 錯誤。默認情況下&#xff0c;此值設置為 25 步。遞歸限制可以在運行時為任何…

08-項目中不可控的任務如何安排和驗收

項目中有時會有一些任務的時間是不可控的&#xff0c;不可控的原因在于該工作完全受制于他人。意思就是如果其他人沒有做好&#xff0c;比如前后端同步開發&#xff0c;前端通常可能會快一些&#xff0c;然后要等后端提供接口&#xff0c;這個時候聯調工作是沒辦法開展的&#…

【Git】git cherry-pick(將某個分支的 commit 改動復制到當前分支)

文章目錄 ?一、基礎用法1.1、?應用單個提交1.2、?應用多個非連續提交1.3、?應用多個連續提交 ?二、高級用法?2.1、沖突處理?2.2、放棄操作?2.3、?不自動提交2.4、應用分支的最新提交 ?一、基礎用法 1.1、?應用單個提交 使用提交的哈希值&#xff08;可通過 git lo…

Milvus WeightedRanker 對比 RRF 重排機制

省流:優先選擇WeightedRanker 以rag為例,優先選擇bm25全文檢索,其次選擇向量檢索 Milvus混合搜索中的重排機制 Milvus通過hybrid_search() API啟用混合搜索功能&#xff0c;結合復雜的重排策略來優化多個AnnSearchRequest實例的搜索結果。本主題涵蓋了重排過程&#xff0c;…

C++手撕共享指針、多線程交替、LRU緩存

1. 共享指針 #include <atomic> #include <iostream>template <typename T> class sharedptr { private:T *ptr;std::atomic<size_t> *count;public:sharedptr(T *p) : ptr(p), count(new std::atomic<size_t>(1)) {}sharedptr(const sharedptr…