自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

本文轉(zhuǎn)載自微信公眾號(hào)「Android開發(fā)編程」,作者Android開發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開發(fā)編程公眾號(hào)。

前言

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

大家都知道RecyclerView有回收復(fù)用機(jī)制,那么回收復(fù)用機(jī)制是如何作用的?

今天我們就用源碼來(lái)講解,一起學(xué)習(xí)

一、Recycler介紹

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

  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對(duì)象有4個(gè)等級(jí),優(yōu)先級(jí)從高到底依次為:

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

2、ArrayList mCachedViews ---- 緩存滑動(dòng)時(shí)即將與RecyclerView分離的ViewHolder,按子View的position或id緩存,默認(rèn)最多存放2個(gè)

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

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

二、緩存機(jī)制分析詳解

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

onLayoutChildren():對(duì)RecyclerView進(jìn)行布局的入口方法

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

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

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

下面主要就來(lái)從看這個(gè)Recycler#getViewForPosition()的實(shí)現(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; 

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

//根據(jù)傳入的position獲取ViewHolder

  1. ViewHolder tryGetViewHolderForPositionByDeadline(int position, 
  2.         boolean dryRun, long deadlineNs) { 
  3.     ...省略     
  4.     boolean fromScrapOrHiddenOrCache = false
  5.     ViewHolder holder = null
  6.     //預(yù)布局 屬于特殊情況 從mChangedScrap中獲取ViewHolder 
  7.     if (mState.isPreLayout()) { 
  8.         holder = getChangedScrapViewForPosition(position); 
  9.         fromScrapOrHiddenOrCache = holder != null
  10.     } 
  11.     if (holder == null) { 
  12.         //1、嘗試從mAttachedScrap中獲取ViewHolder,此時(shí)獲取的是屏幕中可見范圍中的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ā)者實(shí)現(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.                 //如果獲取成功,會(huì)重置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、若以上緩存中都沒有找到對(duì)應(yīng)的ViewHolder,最終會(huì)調(diào)用Adapter中的onCreateViewHolder創(chuàng)建一個(gè) 
  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ù),會(huì)調(diào)用Adapter#onBindViewHolder來(lái)綁定數(shù)據(jù) 
  57.         bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); 
  58.     } 
  59.     ...省略 
  60.     return holder; 

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

總結(jié)

RecyclerView 滑動(dòng)場(chǎng)景下的回收復(fù)用涉及到的結(jié)構(gòu)體兩個(gè):

 

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

 

責(zé)任編輯:武曉燕 來(lái)源: Android開發(fā)編程
相關(guān)推薦

2019-10-18 08:22:43

BIONIOAIO

2017-05-03 17:00:16

Android渲染機(jī)制

2021-09-18 06:56:01

JavaCAS機(jī)制

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2021-09-10 07:31:54

AndroidAppStartup原理

2021-07-22 09:55:28

瀏覽器前端緩存

2018-12-27 12:34:42

HadoopHDFS分布式系統(tǒng)

2019-03-18 09:50:44

Nginx架構(gòu)服務(wù)器

2017-01-13 22:42:15

iosswift

2021-09-16 06:44:04

Android進(jìn)階流程

2021-02-17 11:25:33

前端JavaScriptthis

2021-09-15 07:31:33

Android窗口管理

2017-07-12 14:58:21

AndroidInstant Run

2017-08-15 13:05:58

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

2017-11-14 14:41:11

Java泛型IO

2021-09-24 08:10:40

Java 語(yǔ)言 Java 基礎(chǔ)

2021-09-30 07:36:51

AndroidViewDraw

2023-10-13 13:30:00

MySQL鎖機(jī)制

2024-11-11 17:12:22

2023-06-07 15:34:21

架構(gòu)層次結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)