RecyclerView緩存以及優化—瞭解

歡迎大家入坑.
大家好,我是冰雪情緣,已經在 Android TV開發爬坑多年,也是一名TV開發開源愛好者.

Android TV 開源社區 https://gitee.com/kumei/Android_tv_libs
Android TV 文章專題 https://www.jianshu.com/c/3f0ab61a1322
Android TV QQ羣1:522186932 QQ羣2:468357191

寫在前面

不想看原理的小夥伴,可以直接去看後面優化相關的

爲何要了解ReycclerView的緩存機制,
第一,能更合理的使用緩存,保證應用的流暢性,低耗能;
第二,優化的能更到位;
第三,基礎更紮實,後續 提升技術能力的基石.

在這裏插入圖片描述 在這裏插入圖片描述

當你滿心歡喜 寫了一個類似上圖的 幾個 界面,但是手勢或遙控器 操作的時候,不是卡頓,就是內存,CPU爆炸,是不是一臉矇蔽,然後又無從下手,有那麼幾個函數,又不知道如何使用,性能優化也更無從下手,那麼我們進入正題吧,先去瞭解緩存,再來根據緩存的知識點,進行優化的知識瞭解


理解緩存

簡單瞭解RecyclerView的流程

我們知道 RecyclerView 的優化 最重要是 減少 onCreateViewHolder(縮減createVH), onBindViewHolder(縮減bindVH)耗時(時間)調用次數,下面我們將圍繞着這兩個東西來講解一些的 優化 事宜與相關的 緩存 知識.

先看看 RecyclerView 整個流程的簡單介紹的示意圖:
在這裏插入圖片描述
RecyclerView 包含了很多東西,比如 ItemDecoration,ItemAnimator,LayoutManger,Recycler,ViewHolder,Adapter… …
這裏我們主要講解的是 Recycler 這個東西,它是什麼?
Recycler 主要是負責 ViewHolder 的創建複用(緩存的管理)

可以看看一張ReyclerVie的類圖,大概瞭解下他們之間的關係:
在這裏插入圖片描述
先簡單的看看幾個相關的函數:

RecyclerView相關函數 Recycler 相關函數 含義
setItemViewCacheSize setViewCacheSize 設置 cacheView緩存大小,默認2個
setRecycledViewPool 設置緩存池(RecycledViewPool),每種類型默認5個
setViewCacheExtension 設置自定義緩存
detachAndScrapAttachedViews
setRecyclerListener 設置緩存回調
getViewForPosition 獲取ViewHolder,從緩存中獲取,沒有則創建
bindViewToPosition 綁定 ViewHolder,會調用 Adapter.bindViewHolder -> onBindViewHolder

有沒有思考過,setItemViewCacheSize 什麼時候設置,設置多少個,過程是如何的?
包括 setRecycledViewPool 也是,對於 createView, bindView 他們又有什麼影響?共享 RecycledViewPool 改如何去弄?
如何加快我們的界面加載速度以及複用?
setViewCacheExtension 什麼時候使用?
notify…刷新界面爲何還是要重新創建或者綁定,到底這麼回事?


獲取ViewHolder的流程

先簡單的介紹下獲取ViewHolder的流程
onLayoutChildre 會調用 detachAndScrapAttachedViews 分離 ViewHolder 存儲到相應的緩存(Recycler)中去

這裏拿出了 LinearLayoutManger 佈局的流程(簡單的過程):
fill ->

// layoutChunk 代碼
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
            LayoutState layoutState, LayoutChunkResult result) {
	View view = layoutState.next(recycler);
}

// LayoutState next 代碼
View next(RecyclerView.Recycler recycler) {
    // getViewForPosition 這裏就是獲取 ViewHolder 的過程
    // Recycler 主要是負責 ViewHolder 的 創建 與 複用(緩存的管理)
	final View view = recycler.getViewForPosition(mCurrentPosition);
    mCurrentPosition += mItemDirection;
    return view;
}

那我們先來看看getViewForPosition獲取ViewHolder的流程吧:
在這裏插入圖片描述
getViewForPosition 這個函數就是從 Recycler 取出 ViewHolder 交給 Adapter.
getViewForPosition 做了哪些事情?按照上面流程我們大概的瞭解到,就是將 Recycler 緩存中對應的 ViewHolder取出來,沒有則重新調用 createVH 創建ViewHolder.

Recylcer 緩存包含了(ChangedScrapAttachedScrapCachedViewsViewCacheExtensionRecyclerPool),後面會詳解去了解緩存相關的知識,我們先來看看代碼的過程

// 按照流程圖一步步看下去,我們一共分成了 7 步
ViewHolder holder = null;
// 第 1 步: 根據 position 獲取 changedScrap(被更新的ViewHolder)
if (mState.isPreLayout()) {
	holder = getChangedScrapViewForPosition(position);
	fromScrapOrHiddenOrCache = holder != null;
}

// 第 2 步:根據 position 獲取 AttachedScrap(還在屏幕中的ViewHolder),CachedViews(緩存離開屏幕的viewHolder)
if (holder == null) {
	holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}

// 第 3 步:根據 ItemId 獲取 AttachedScrap,CachedViews
if (holder == null) {
	final int type = mAdapter.getItemViewType(offsetPosition);
	// 判斷是否開啓了 StableIds,如果要開啓,需要調用 Adapter.setHasStableIds(true)
	if (mAdapter.hasStableIds()) {
		holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun);
	}
}

// 第 4 步:根據 position, type 從 自定義緩存 獲取 ViewHolder
if (holder == null && mViewCacheExtension != null) {
	final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
	if (view != null) {
    	holder = getChildViewHolder(view);
    }
}

// 第 5 步:根據 type 從 RecycledViewPool 獲取對應的 ViewHolder
if (holder == null) {
	holder = getRecycledViewPool().getRecycledView(type);
}

// 第 6 步:創建 ViewHolder
if (holder == null) {
	holder = mAdapter.createViewHolder(RecyclerView.this, type);
}

// 第 7 步:判斷是否需要 bindViewHolder
if (mState.isPreLayout() && holder.isBound()) {
	// do not update unless we absolutely have to.
	holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
	// 判斷 是否bind過 或 是否已更新 或 是否失效
	// tryBindViewHolderByDeadline
	mAdapter.bindViewHolder(holder, offsetPosition);
}

上面已經大概的講解了 getViewForPosition 從 Recycler 獲取 ViewHolder 的過程,

這裏我們大概瞭解下 Recycler 的幾個緩存變量:

public final class Recycler {
	private ArrayList<ViewHolder> mChangedScrap = null; 
	final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();      
	final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>(); 
	private ViewCacheExtension mViewCacheExtension;
	private RecycledViewPool mRecyclerPool;
} 

這幾個緩存變量有什麼區別?
這是優先級最高的緩存,RecyclerView在獲取ViewHolder時,優先會到這 mAttachedScrap 與 mChangedScrap 兩個緩存來找。其次纔是 mCachedViews,最後纔是RecyclerViewPool。

列出一個表格說明對應的意思(需要注意create,bind,優化相關):

緩存級別 createVH bindVH 變量 含義
一級緩存(Scrap View) mAttachedScrap mAttachedScrap存儲的是當前還在屏幕中的ViewHolder。匹配機制按照position和id進行匹配
一級緩存(Scrap View) mChangedScrap mChangedScrap存儲的是數據被更新的ViewHolder,比如說調用了Adapter的 notifyXXX 方法
二級緩存(Cache View) mCachedViews 默認大小爲2,緩存離開屏幕的viewHolder. 解決兩點: 1. 頻繁進入/離開屏幕的ViewHolder導致的內存抖動的問題;2.還有用於保存Prefetch的ViewHoder.
三級緩存(可選可配置) ViewCacheExtension 自定義緩存,通常用不到,getViewForPositionAndType 來實現自己的緩存 使用場景:位置固定 內容不變 數量有限
四級緩存(緩存池) RecyclerViewPool 根據ViewType來緩存ViewHolder,每個ViewType的數組大小默認爲5,可以動態的改變 緩存的ViewHolder需要重新綁定(bindView). 也可以 RecyclerView之間共享ViewHolder的緩存池Pool.

其實再 bindView之前,有一個判斷 !holder.isBound() || holder.needsUpdate() || holder.isInvalid(),這幾個是什麼意思呢?

判斷是否要重新綁定 ViewHolder,holder.isBound() || holder.needsUpdate() || holder.isInvalid(),下列看看全部相關意義。

ViewHolder的 isInvalidisRemovedisBoundisTmpDetachedisScrapisUpdated 這幾個方法:

方法名 對應的Flag 含義或者狀態設置的時機
isInvalid FLAG_INVALID 表示當前ViewHolder是否已經失效。通常來說,在3種情況下會出現這種情況:1.調用了Adapter的notifyDataSetChanged方法; 2. 手動調用RecyclerView的invalidateItemDecorations方法; 3. 調用RecyclerView的setAdapter方法或者swapAdapter方法。
isRemoved FLAG_REMOVED 表示當前的ViewHolder是否被移除。通常來說,數據源被移除了部分數據,然後調用Adapter的notifyItemRemoved方法。
isBound FLAG_BOUND 表示當前ViewHolder是否已經調用了onBindViewHolder。
isTmpDetached FLAG_TMP_DETACHED 表示當前的ItemView是否從RecyclerView(即父View)detach掉。通常來說有兩種情況下會出現這種情況:1.手動了RecyclerView的detachView相關方法;2. 在從mHideViews裏面獲取ViewHolder,會先detach掉這個ViewHolder關聯的ItemView
isScrap 無Flag來表示該狀態,用mScrapContainer是否爲null來判斷 表示是否在mAttachedScrap或者mChangedScrap數組裏面,進而表示當前ViewHolder是否被廢棄。
isUpdated FLAG_UPDATE 表示當前ViewHolder是否已經更新。通常來說,在3種情況下會出現情況:1.isInvalid方法存在的三種情況;2.調用了Adapter的onBindViewHolder方法;3. 調用了Adapter的notifyItemChanged方法

回收相關API

剛纔在 onLayoutChildre 的 detachAndScrapAttachedViews 這個函數很關鍵,分離了 屏幕上 ViewHolder 存儲到對應的緩存上。這裏再普及下其它相關的函數:

方法名 含義
detachAndScrapAttachedViews(recycler) detach輕量回收所有View
detachAndScrapView(view, recycler) detach輕量回收指定View
detachView(view); 超級輕量回收一個View,馬上就要添加回來
attachView(view); 將上個方法detach的View attach回來
recycler.recycleView(viewCache.valueAt(i)); detachView 後 沒有attachView的話 就要真的回收掉他們

recycle真的回收一個View ,該View再次回來需要執行onBindViewHolder方法
removeAndRecycleView(View child, Recycler recycler)
removeAndRecycleAllViews(Recycler recycler);

detach 和recycle的時機
一個View只是暫時被清除掉,稍後立刻就要用到,使用detach。它會被緩存進scrapCache的區域。
一個View 不再顯示在屏幕上,需要被清除掉,並且下次再顯示它的時機目前未知 ,使用remove。它會被以viewType分組,緩存進RecyclerViewPool裏。

瞭解detach/attach View
addView和removeView方法,操作容器內的子視圖數組,觸發視圖重繪製,觸發子視圖attach和detached窗體回調。
addViewInLayout和removeViewInLayou方法,與上面一樣,只是不會重繪視圖
attachViewToParent和detachViewFromParent方法,只會操作容器內的子視圖數組

注意: 一個View只被detach,沒有被recycle的話,不會放進RecyclerViewPool裏,會一直存在recycler的scrap 中。網上有人的Demo就是如此,因此View也沒有被複用,有多少ItemCount,就會new出多少個ViewHolder。

題外話:想了解的 自定義 Layoutmanger,可以去了解下 onLayoutChildren
進行自定義的佈局之前:

  1. 調用detachAndScrapAttachedViews方法把屏幕中的Items都分離出來,內部調整好位置和數據後,
    detachAndScrapAttachedViews(recycler)這個方法就是將屏幕上可見的view緩存在scrap裏 。
    先把所有的View先從RecyclerView中detach掉,然後標記爲"Scrap"狀態,表示這些View處於可被重用狀態(非顯示中)。
    實際就是把View放到了Recycler中的一個集合中。
    detachAndScrapAttachedViews()會根據情況,將原來的Item View放入Scrap HeapRecycle Pool,從而在複用時提升效率。
  2. 調用 Recycler的getViewForPosition(int position) 方法來獲取,通過addView方法來添加.
  3. 獲取到Item並重新添加了之後,需要對它進行測量,這時候可以調用measureChild或measureChildWithMargins方法,
  4. 根據需求來決定使用 layoutDecorated 還是 layoutDecoratedWithMargins 方法;

在自定義ViewGroup中,layout完就可以運行看效果了,但在LayoutManager還有一件非常重要的事情,就是回收了,我們在layout之後,還要把一些不再需要的Items回收,以保證滑動的流暢度;
自定義LayoutManger,可以瞭解這篇文章


刷新界面-緩存的處理

notifyItemChanged對緩存的影響

同樣只更新pos=1的數據,使用 notifyItemChanged(position)notifyDataSetChanged() 有什麼區別?

notifyItemChanged(position)
在這裏插入圖片描述
notifyDataSetChanged()
在這裏插入圖片描述

名稱 區別
notifyItemChanged(position),notifyItemChanged(int position, Object payload) 等 被更新的的item放入了ChangeScrp,不需要createVH,但是需要bindVH
notifyDataSetChanged() 0~4放入了RecyclerPool,不需要createVH,但是全部需要 bindVH

滾動-緩存的處理

往上滑動的緩存過程
在這裏插入圖片描述


優化了解

概述

根據 Recycler 獲取或者存儲 緩存的流程,我們知道 RecyclerView 優化 最重要是 減少 createViewHolder, bindViewHolder耗時(時間)調用次數,下面我們將圍繞着兩個東西來講解下一些簡單的優化事宜.
在這裏插入圖片描述

onCreateViewHolder
在這裏插入圖片描述
onBindViewHolder
在這裏插入圖片描述
想詳細瞭解繪製過度,耗時查找等優化事宜,參考這篇博客-性能優化學習筆記.
一些函數相關的Demo 可以看看這個

根據上面的兩個內容再補充一些細節:

  • 合理使用緩存設置(setItemViewCacheSize,setViewCacheExtension,setRecycledViewPool)
  • 注意耗時操作(尤其是屏幕滾動的時候,儘量 停止加載的操作)
  • 減少佈局結構、減少過渡繪製,可以提高item的 measure 與 draw 的效率。也儘量避免多次measure & layout 次數(比如TextView可以進行有效優化)
    TextView 可以使用 使用 StaticLayout 或者 DynamicLayout 的自定義 View 來代替它
  • 取消默認動畫
    mRecyclerView.setItemAnimator(null); 也可以改善一點點.
  • 調整draw緩存
    mRecyclerView.setDrawingCacheEnabled(true); mRecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
  • 慎用Alpha(不管是圖片還是View,都需要注意下)
    也可以嘗試重寫 View 的 hasOverlappingRendering return false,提升一點點性能
  • 儘量使用穩定的高版本RecyclerView,比如新版本(25.1.0 及以上)有 Prefetch 功能
  • Item 高度是固定的話,RecyclerView.setHasFixedSize(true)
  • onViewRecycled 可以回收一些資源.
  • 設置更多預留空間(屏幕顯示範圍之外),重寫 getExtraLayoutSpace
    啓動應用後,如果一整屏 item的時候,向下滑動,RecyclerView找不到緩存,它將創建一個新的item,導致有點延時的感覺.
  • RecyclerView沒有ItemClick方法,根據前面緩存的瞭解,建議 onCreateViewHolder 添加一次,避免多次調用 onBindViewHolder。
  • diffutil 可以瞭解下,是一個不錯的工具,可以 判斷兩個數據集的差距
  • swapadapter 也可以瞭解下. 以前 setAdapter 是要清空緩存的,可以很好的發揮緩存的性能. 應用場景是兩個數據源有很大的相似部分的情況下。
  • linearlayoutmanager的onSaveInstanceState和onRestoreInstanceState可以瞭解下.
  • 根據不同的機型(CPU,內存,內存使用情況 等等) 特性 進行對應的 優化策略(空間與時間 不可能達到100%完美平衡)
    也可以瞭解下這個幾個函數(onTrimMemory,onLowMemory)

onTrimMemory使用資料

onTrimMemory(int level) :
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN: // 你的用戶界面不再可見,此時應該釋放僅僅使用在UI上的大資源
ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE: // 系統處於低內存狀態,app正在運行且不會被殺死
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND: // 系統正處於低內存狀態,app進程位於LRU List開始附近,儘管app進程被殺死的概率低,系統可能已經開始殺在LRU List中的後臺進程,此時應該釋放一些資源,防止被殺的機率
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: // 系統處於更低內存狀態,app正在運行且不會被殺死,可以釋放不用的資源提升app性能
ComponentCallbacks2.TRIM_MEMORY_MODERATE: //  系統正處於低內存狀態,app進程位於LRU List中部附近,如果系統進一步內存緊張,app進程可能會被殺掉
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: // 系統處於極低的內存狀態,app仍然不會被殺死。但如果不是放資源,系統開始殺後臺進程,此時app應該清理一些資源
ComponentCallbacks2.TRIM_MEMORY_COMPLETE: // 系統正處於低內存狀態,app進程是首先被殺進程之一,如果系統現在沒有恢復內存,應該立即釋放對app不重要的所有內容

RecyclerViewPool 注意事項!!!

我在測試代碼裏面 共用了一個 RecyclerViewPool 。

// 這裏需要,因爲使用 RecyclerViewPool,是以空間換時間,
// 需要注意內存情況,在內存低的情況,可以嘗試清理掉.
int max = 8; 
mRecycledViewPool.setMaxRecycledViews(TVUICommonItemView.ITEM_TYPE_1, max);
mRecycledViewPool.setMaxRecycledViews(TVUICommonItemView.ITEM_TYPE_2, max);
mRecycledViewPool.setMaxRecycledViews(TVUICommonItemView.ITEM_TYPE_3, max);
mRecycledViewPool.setMaxRecycledViews(TVUICommonItemView.ITEM_TYPE_4, max);
mRecycledViewPool.setMaxRecycledViews(TVUICommonItemView.ITEM_TYPE_5, max);

共用 RecyclerViewPool 注意幾個問題
在這裏插入圖片描述
類型(Type)爲0 的ItemView 還是在調用 onCreateViewHolder 的,一共調用了12次.
因爲並沒有使用 Pool裏面的緩存,爲何,裏面沒有東西哈,取不出來holder = getRecycledViewPool().getRecycledView(type);,holder 爲 null。所以第一次顯示界面,還是在拼命的創建,這裏讓人很沮喪!!!
在這裏插入圖片描述
所以結論就是,雖然共用了RecyclerViewPool ,第一次加載頁面 或者 頁面快速滾動 又或者 想使用緩存Pool的時候,如果Pool裏面沒有,就需要重新創建。

反觀使用 Leanback 雖然也是共用 RecyclerViewPool,但 在滾動的過程中,如果之前的 Pool沒有緩存對應的類型,也是白塔,還是需要創建大量的ItemView,反正是挺耗時的.

當有多個頁面的切換的時候,如果另一個頁面的 RecyclerView被釋放掉了. 它頁面的之前的View會被清理掉. 下次再創建這個頁面,進入,如果沒有緩存使用,還是會進行創建.
在這裏插入圖片描述

再前面 我們已經分析過,當離開屏幕的Item會保存(putRecycledView)到對應的 類型下的緩存Pool(mScrap)裏面。

如何加速第一次加載的優化
這裏也是用空間換時間,避免 創建視圖耗費的時間,進而可以快速的顯示界面.
提前創建緩衝池的 ViewHolder,這樣可以減少 onCreateViewHolder,但是還是需要 onBindViewHolder

// 看個簡單的小栗子
recycledViewPool.putRecycledView(adapter.createViewHolder(recyclerView, TYPE_1));
recycledViewPool.putRecycledView(adapter.createViewHolder(recyclerView, TYPE_1));
recycledViewPool.putRecycledView(adapter.createViewHolder(recyclerView, TYPE_2));

第一次進入界面會連續調用 onCreateViewHolder,因爲 onCreateViewHolder沒有辦法去設置 ItemType,所以需要調用 createViewHolder

注意getItemViewType
當很多不同類型的控件的時候,不設置 ItemType返回的話,或者只返回 position,界面肯定會錯亂,也達不到優化的好處.

在 Adapter 的 getItemViewType 設置對應的類型
@Override
public int getItemViewType(int position) {
	return mDatas.get(position).getItemType();
}

因爲緩存裏面會根據 Type 去取:holder = getRecycledViewPool().getRecycledView(type);
所以添加緩存的大小以及類型的也需要注意,和你的不同類型的控件記得要對應上。

mRecycledViewPool.setMaxRecycledViews(ItemView.TYPE_1, 10);

注意getItemId的使用
調用 notifyDataSetChanged 的時候,recyclerView 不知道到底發生了什麼,所以它只能認爲所有的東西都發生了變化,將所有的 viewHolder 都放入到 pool 中。

位置固定,比如,廣告位。
不會改變
數量合理,保存在內存中沒啥關係。

// Adapter.setHasStableIds(true);
// mAdapter.hasStableIds()
// 當設置了以上的 開關後,detachAndScrapAttachedViews -> scrapOrRecycleView 的 recycler.scrapView(view); 
// 將緩存到 mAttachedScrap,最後直接使用,不需要創建,綁定數據,非常優化喔!!
public long getItemId(int position) {
    Data data = datas.get(position);
    return (data.getTitle()).hashCode();
}
// 當你的標題欄被修改了,調用 notifydatasetchanged 刷新.
// 如果沒有被更新,就會使用緩存,相當的優化.

// 或者一般這樣寫
 @Override
public long getItemId(int position) {
	return position;
}

思考:如果 getScrapOrCachedViewForId 去 獲取(getItemId )的 Id 被改變了,那麼 tryGetViewHolderForPositionByDeadline 接下來如何執行?自己思考下?然後單獨

onViewRecycled的使用
View 被回收的時候調用,所以在這個函數裏面可以釋放資源;
比如釋放一些圖片資源,Glid,ImageView等等.

思考:爲何這裏可以釋放掉不要的東西?
答:因爲 addViewHolderToRecycledViewPool -> dispatchViewRecycled -> mAdapter.onViewRecycled,所以 ViewHolder 加入了 RecycledViewPool,複用的時候,不需要創建View,但是需要重新綁定數據.

CacheView使用注意
CacheView 默認爲2個,主要是用來保存臨時滾動屏幕外的 ViewHolder,主要是爲了避免內存抖動.
比如你上下滾動又回來啦,只是短暫移除屏幕,又馬上使用了,這裏就不需要重新 創建 與 綁定.
CacheView 如果需要使用,其實就是 用空間換時間. 因爲是實實在在的存在數組裏面的.

RecyclerView的單擊事件如何加?

一般人都會將單擊事件加在 onBindViewHolder裏面,這裏是錯誤的行爲!因爲我們分析過 CacheView默認只有兩個,如果新出的View能用到這個緩存,當然不用重新的 創建,綁定;但是大部分時間都是在使用 RecyclerPool,所以需要綁定數據,這就導致,頻繁的 單擊事件添加,內存抖動,這裏處理的方案是,在 ViewHolder裏面添加。

public static class ColorViewHolder extends RecyclerView.ViewHolder {
        public ColorViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(v.getContext(), getLayoutPosition(), Toast.LENGTH_LONG).show();
                }
            });
        }

LinearLayoutMaanager.setInitialPrefetchItemCount()

RecyclerView.setHasFixedSize(true)
局部刷新,避免 false 會觸發,requestLayut()會重新走三大流程(onMeasure,onLayout,onDraw)
在這裏插入圖片描述

多個RecyclerView公用 RecyclerViewPool

根據設備的內存,CPU情況自動設定優化策略


參考資料

自定義LayoutManger,可以瞭解這篇文章
http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/

深入淺出RecyclerView
https://kymjs.com/code/2016/07/10/01/

Anatomy of RecyclerView: a Search for a ViewHolder (continued) [需要翻牆]
https://android.jlelse.eu/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-continued-d81c631a2b91

RecyclerView緩存原理,有圖有真相
https://juejin.im/post/5b79a0b851882542b13d204b

RecyclerView緩存機制(咋複用?)
https://github.com/MicroKibaco/CrazyDailyQuestion/issues/7

真正帶你搞懂 RecyclerView 的緩存機制
https://zhuanlan.zhihu.com/p/80475040

RecyclerView-LayoutManger相關
RecyclerView解析之LinearLayoutManager

結束語

由於本人技術水平有限,有問題的地方還望一起探討,學習,互相 進步,謝謝.
不要忘記三連,點贊,關注,收藏.
更歡迎加入TV社區, QQ羣 與 關注TV文章專題.

Android TV 開源社區 https://gitee.com/kumei/Android_tv_libs
Android TV 文章專題 https://www.jianshu.com/c/3f0ab61a1322
Android TV QQ羣1:522186932 QQ羣2:468357191

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章