成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android架構師之深入理解RecyclerView復用和緩存機制詳解

移動開發 Android
RecyclerView滑動時會觸發onTouchEvent#onMove,回收及復用ViewHolder在這里就會開始。我們知道設置RecyclerView時需要設置LayoutManager,LayoutManager負責RecyclerView的布局,包含對ItemView的獲取與復用。

本文轉載自微信公眾號「Android開發編程」,作者Android開發編程。轉載本文請聯系Android開發編程公眾號。

前言

學習源碼,研究源碼編程思想,是程序開發者進階的必經之路

大家都知道RecyclerView有回收復用機制,那么回收復用機制是如何作用的?

今天我們就用源碼來講解,一起學習

一、Recycler介紹

RecyclerView是通過內部類Recycler管理的緩存,那么Recycler中緩存的是什么?我們知道RecyclerView在存在大量數據時依然可以滑動的如絲滑般順暢,而RecyclerView本身是一個ViewGroup,那么滑動時避免不了添加或移除子View(子View通過RecyclerView#Adapter中的onCreateViewHolder創建),如果每次使用子View都要去重新創建,肯定會影響滑動的流 暢性,所以RecyclerView通過Recycler來緩存的是ViewHolder(內部包含子View),這樣在滑動時可以復用子View,某些條件下還可以復用子View綁定的數據。所以本質上緩存是為了減少重復繪制View和綁定數據的時間,從而提高了滑動時的性能

  1. public final class Recycler { 
  2.         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>(); 
  3.         ArrayList<ViewHolder> mChangedScrap = null
  4.         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>(); 
  5.         private final List<ViewHolder> 
  6.                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap); 
  7.         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE; 
  8.         int mViewCacheMax = DEFAULT_CACHE_SIZE; 
  9.         RecycledViewPool mRecyclerPool; 
  10.         private ViewCacheExtension mViewCacheExtension; 
  11.         static final int DEFAULT_CACHE_SIZE = 2; 

Recycler緩存ViewHolder對象有4個等級,優先級從高到底依次為:

1、ArrayList mAttachedScrap --- 緩存屏幕中可見范圍的ViewHolder

2、ArrayList mCachedViews ---- 緩存滑動時即將與RecyclerView分離的ViewHolder,按子View的position或id緩存,默認最多存放2個

3、ViewCacheExtension mViewCacheExtension --- 開發者自行實現的緩存

4、RecycledViewPool mRecyclerPool --- ViewHolder緩存池,本質上是一個SparseArray,其中key是ViewType(int類型),value存放的是 ArrayList< ViewHolder>,默認每個ArrayList中最多存放5個ViewHolder。

二、緩存機制分析詳解

RecyclerView滑動時會觸發onTouchEvent#onMove,回收及復用ViewHolder在這里就會開始。我們知道設置RecyclerView時需要設置LayoutManager,LayoutManager負責RecyclerView的布局,包含對ItemView的獲取與復用。以LinearLayoutManager為例,當RecyclerView重新布局時會依次執行下面幾個方法:

onLayoutChildren():對RecyclerView進行布局的入口方法

fill(): 負責對剩余空間不斷地填充,調用的方法是layoutChunk()

layoutChunk():負責填充View,該View最終是通過在緩存類Recycler中找到合適的View的

上述的整個調用鏈:onLayoutChildren()->fill()->layoutChunk()->next()->getViewForPosition(),getViewForPosition()即是是從RecyclerView的回收機制實現類Recycler中獲取合適的View,

下面主要就來從看這個Recycler#getViewForPosition()的實現。

  1. @NonNull 
  2. public View getViewForPosition(int position) { 
  3.     return getViewForPosition(position, false); 
  4. View getViewForPosition(int position, boolean dryRun) { 
  5.     return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView; 

他們都會執行tryGetViewHolderForPositionByDeadline函數,繼續跟進去:

//根據傳入的position獲取ViewHolder

  1. ViewHolder tryGetViewHolderForPositionByDeadline(int position, 
  2.         boolean dryRun, long deadlineNs) { 
  3.     ...省略     
  4.     boolean fromScrapOrHiddenOrCache = false
  5.     ViewHolder holder = null
  6.     //預布局 屬于特殊情況 從mChangedScrap中獲取ViewHolder 
  7.     if (mState.isPreLayout()) { 
  8.         holder = getChangedScrapViewForPosition(position); 
  9.         fromScrapOrHiddenOrCache = holder != null
  10.     } 
  11.     if (holder == null) { 
  12.         //1、嘗試從mAttachedScrap中獲取ViewHolder,此時獲取的是屏幕中可見范圍中的ViewHolder 
  13.         //2、mAttachedScrap緩存中沒有的話,繼續從mCachedViews嘗試獲取ViewHolder 
  14.         holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); 
  15.      ...省略 
  16.     } 
  17.     if (holder == null) { 
  18.         final int offsetPosition = mAdapterHelper.findPositionOffset(position); 
  19.         ...省略 
  20.         final int type = mAdapter.getItemViewType(offsetPosition); 
  21.         //如果Adapter中聲明了Id,嘗試從id中獲取,這里不屬于緩存 
  22.         if (mAdapter.hasStableIds()) { 
  23.             holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), 
  24.                     type, dryRun); 
  25.         } 
  26.         if (holder == null && mViewCacheExtension != null) { 
  27.             3、從自定義緩存mViewCacheExtension中嘗試獲取ViewHolder,該緩存需要開發者實現 
  28.             final View view = mViewCacheExtension 
  29.                     .getViewForPositionAndType(this, position, type); 
  30.             if (view != null) { 
  31.                 holder = getChildViewHolder(view); 
  32.             } 
  33.         } 
  34.         if (holder == null) { // fallback to pool 
  35.             //4、從緩存池mRecyclerPool中嘗試獲取ViewHolder 
  36.             holder = getRecycledViewPool().getRecycledView(type); 
  37.             if (holder != null) { 
  38.                 //如果獲取成功,會重置ViewHolder狀態,所以需要重新執行Adapter#onBindViewHolder綁定數據 
  39.                 holder.resetInternal(); 
  40.                 if (FORCE_INVALIDATE_DISPLAY_LIST) { 
  41.                     invalidateDisplayListInt(holder); 
  42.                 } 
  43.             } 
  44.         } 
  45.         if (holder == null) { 
  46.             ...省略 
  47.           //5、若以上緩存中都沒有找到對應的ViewHolder,最終會調用Adapter中的onCreateViewHolder創建一個 
  48.             holder = mAdapter.createViewHolder(RecyclerView.this, type); 
  49.         } 
  50.     } 
  51.     boolean bound = false
  52.     if (mState.isPreLayout() && holder.isBound()) { 
  53.         holder.mPreLayoutPosition = position; 
  54.     } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { 
  55.         final int offsetPosition = mAdapterHelper.findPositionOffset(position); 
  56.         //6、如果需要綁定數據,會調用Adapter#onBindViewHolder來綁定數據 
  57.         bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); 
  58.     } 
  59.     ...省略 
  60.     return holder; 

通過mAttachedScrap、mCachedViews及mViewCacheExtension獲取的ViewHolder不需要重新創建布局及綁定數據;通過緩存池mRecyclerPool獲取的ViewHolder不需要重新創建布局,但是需要重新綁定數據;如果上述緩存中都沒有獲取到目標ViewHolder,那么就會回調Adapter#onCreateViewHolder創建布局,以及回調Adapter#onBindViewHolder來綁定數據

總結

RecyclerView 滑動場景下的回收復用涉及到的結構體兩個:

 

  • mCachedViews 和 RecyclerViewPool
  • mCachedViews 優先級高于 RecyclerViewPool,回收時,最新的 ViewHolder 都是往 mCachedViews 里放,如果它滿了,那就移出一個扔到 ViewPool 里好空出位置來緩存最新的 ViewHolder。
  • 復用時,也是先到 mCachedViews 里找 ViewHolder,但需要各種匹配條件,概括一下就是只有原來位置的卡位可以復用存在 mCachedViews 里的 ViewHolder,如果 mCachedViews 里沒有,那么才去 ViewPool 里找。
  • 在 ViewPool 里的 ViewHolder 都是跟全新的 ViewHolder 一樣,只要 type 一樣,有找到,就可以拿出來復用,重新綁定下數據即可。

 

責任編輯:武曉燕 來源: Android開發編程
相關推薦

2019-10-18 08:22:43

BIONIOAIO

2021-09-18 06:56:01

JavaCAS機制

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2017-05-03 17:00:16

Android渲染機制

2021-07-22 09:55:28

瀏覽器前端緩存

2021-09-10 07:31:54

AndroidAppStartup原理

2018-12-27 12:34:42

HadoopHDFS分布式系統

2019-03-18 09:50:44

Nginx架構服務器

2017-01-13 22:42:15

iosswift

2021-02-17 11:25:33

前端JavaScriptthis

2021-09-16 06:44:04

Android進階流程

2017-08-15 13:05:58

Serverless架構開發運維

2017-11-14 14:41:11

Java泛型IO

2017-07-12 14:58:21

AndroidInstant Run

2021-09-15 07:31:33

Android窗口管理

2021-09-24 08:10:40

Java 語言 Java 基礎

2023-10-13 13:30:00

MySQL鎖機制

2021-09-30 07:36:51

AndroidViewDraw

2023-06-07 15:34:21

架構層次結構

2024-11-11 17:12:22

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品无码久久久久 | 精品国产乱码久久久久久久久 | 成人国产精品色哟哟 | 精品欧美一区二区三区 | 亚洲不卡在线观看 | 欧美精品久久久 | 国产精品日韩一区二区 | 精品中文字幕在线 | 日韩国产欧美在线观看 | 精品一区二区免费视频 | 午夜久久久| 国产极品粉嫩美女呻吟在线看人 | 成人在线一区二区 | 午夜影院在线观看 | 51ⅴ精品国产91久久久久久 | 欧美日韩综合 | 亚洲一区精品在线 | 亚洲欧美精品 | 亚洲精品在 | 日韩精品免费在线观看 | 亚洲精品乱码久久久久久久久 | 一区二区在线免费观看 | 亚洲精品自在在线观看 | 精品欧美一区二区三区久久久 | 亚洲播放 | 五月激情婷婷在线 | 久久久久成人精品亚洲国产 | 在线成人av | 国产精品久久av | 亚洲一区二区免费 | 在线免费观看毛片 | 欧美日韩在线免费观看 | 国产精品永久免费视频 | 午夜在线精品 | 日日夜夜天天 | 农村真人裸体丰满少妇毛片 | 国产日韩欧美一区 | 国产乱码精品1区2区3区 | 中文字幕av网站 | 韩国久久| 欧美黄色小视频 |