偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Android架構(gòu)師之深入理解RecyclerView復用和緩存機制詳解

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

本文轉(zhuǎn)載自微信公眾號「Android開發(fā)編程」,作者Android開發(fā)編程。轉(zhuǎn)載本文請聯(lián)系Android開發(fā)編程公眾號。

前言

學習源碼,研究源碼編程思想,是程序開發(fā)者進階的必經(jīng)之路

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

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

一、Recycler介紹

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

  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個等級,優(yōu)先級從高到底依次為:

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

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

3、ViewCacheExtension mViewCacheExtension --- 開發(fā)者自行實現(xiàn)的緩存

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

二、緩存機制分析詳解

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

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

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

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

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

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

  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; 

他們都會執(zhí)行tryGetViewHolderForPositionByDeadline函數(shù),繼續(xù)跟進去:

//根據(jù)傳入的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緩存中沒有的話,繼續(xù)從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,該緩存需要開發(fā)者實現(xiàn) 
  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狀態(tài),所以需要重新執(zhí)行Adapter#onBindViewHolder綁定數(shù)據(jù) 
  39.                 holder.resetInternal(); 
  40.                 if (FORCE_INVALIDATE_DISPLAY_LIST) { 
  41.                     invalidateDisplayListInt(holder); 
  42.                 } 
  43.             } 
  44.         } 
  45.         if (holder == null) { 
  46.             ...省略 
  47.           //5、若以上緩存中都沒有找到對應的ViewHolder,最終會調(diào)用Adapter中的onCreateViewHolder創(chuàng)建一個 
  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、如果需要綁定數(shù)據(jù),會調(diào)用Adapter#onBindViewHolder來綁定數(shù)據(jù) 
  57.         bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); 
  58.     } 
  59.     ...省略 
  60.     return holder; 

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

總結(jié)

RecyclerView 滑動場景下的回收復用涉及到的結(jié)構(gòu)體兩個:

 

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

 

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

2021-09-18 06:56:01

JavaCAS機制

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2021-07-22 09:55:28

瀏覽器前端緩存

2019-10-18 08:22:43

BIONIOAIO

2017-05-03 17:00:16

Android渲染機制

2021-09-10 07:31:54

AndroidAppStartup原理

2018-12-27 12:34:42

HadoopHDFS分布式系統(tǒng)

2019-03-18 09:50:44

Nginx架構(gòu)服務器

2017-01-13 22:42:15

iosswift

2017-08-15 13:05:58

Serverless架構(gòu)開發(fā)運維

2021-02-17 11:25:33

前端JavaScriptthis

2017-11-14 14:41:11

Java泛型IO

2021-09-16 06:44:04

Android進階流程

2021-09-24 08:10:40

Java 語言 Java 基礎

2017-07-12 14:58:21

AndroidInstant Run

2021-09-15 07:31:33

Android窗口管理

2023-10-13 13:30:00

MySQL鎖機制

2021-09-30 07:36:51

AndroidViewDraw

2023-06-07 15:34:21

架構(gòu)層次結(jié)構(gòu)

2024-11-11 17:12:22

點贊
收藏

51CTO技術棧公眾號