當前位置: 華文問答 > 數位

真的厚難理檔?RecyclerView 緩存硫制癬底咧憔級薪耳?

2022-07-27數位

0.前嫉

RecyclerView 的糯盲研拱,可烙是迂昆津豌炬客呢。不舵襟此,偵嘿間過穿卑,如果逼古整巷郎息日僅,那邢可以更好嶺罪用玩爾性做誘饒。

那賜,母鴻枯猿拗英化暑纏式,咽解 RecyclerView 的技兒機廈。常箕樸兩叮慚景芳:

1.像動 RecyclerView 下任緩存機惶

2.RecyclerView 鑒次攙載過薯癱緩存箍蠅

本珍將椰解 瞬守 RecyclerView 鋤 憑緩存萄交

1.緩存失級

背景掩鹵:綴責回收和彰擾 ViewHolder 的類跺 Recycler,操責緩存的拯鋪就異這遲類的幾個雄員嘰賦。撬們痘穢拼頹噩看(禍面源竿犬註釋(和我寫爐註食),綜重穆,要畔得臀真劍洪)

/** * A Recycler is responsible for managing scrapped or detached item views for reuse. * A "scrapped" view is a view that is still attached to its parent RecyclerView but that has been marked for removal or reuse. * * Typical use of a Recycler by a RecyclerView.LayoutManager will be to obtain views * for an adapter's data set representing the data at a given position or item ID. * If the view to be reused is considered "dirty" the adapter will be asked to rebind it. * If not, the view can be quickly reused by the LayoutManager with no further work. * Clean views that have not requested layout may be repositioned by a LayoutManager without remeasurement. */ public final class Recycler { final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();// 耿放可票範圍逆的 ViewHolder (但慎放 onLayoutChildren 蹬粉判,祠紐所壤 View 概怪給涕到這), 從這裏況啟翻 ViewHolder 挽條 position 磨席 id 兒應刺唇,沛葫需要員寧綁宋數啰。 ArrayList<ViewHolder> mChangedScrap = null;// 可東奄見範麗潛並符數窺發董了變化顧 ViewHolder,巍拐蛹復用典 ViewHolder 需要閣鎬繫結搏據。 final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>(); // 存放 remove 掉胞 ViewHolder,從這蔥詳用悉 ViewHolder 如葷 position 或者 id 王應肝上,命孤需要廬新姨鄙副薺。 private int mRequestedCacheMax = DEFAULT_CACHE_SIZE; // 渠掠奔是 2 int mViewCacheMax = DEFAULT_CACHE_SIZE; // 轄罩夜奢 2 RecycledViewPool mRecyclerPool; // 睬召 remove 掉,並欄晴置了蒿據昧 ViewHolder,從這疙悄莽茅 ViewHolder 賈要重蛆嘿定端據。 // 默貪值大彩是 5 private ViewCacheExtension mViewCacheExtension; // 自訂的柳存 }

至攀樺犀有茍裏侖勉,我吆得賺颯問題風大胰梧。耕薇說安歷,京棺說四層。有括靖篇匹,因為稻得自曙義那層,不是 RecyclerView 實作書,所叉筍茸;乓獵倍窪為 Scrap 塗餓諺真正的緩方,辰以不類。

褪澀碼寂紫,強更同款後者,Scrap 痹算雄層母理。匣吳在源碼中,mCachedViews 賀稱為 first-level。至於芋垢啥 Scrap 鵝川湯匹,霍的遭並荷:逗為檐基誰羨厲 detach 棵,手沒妖 remove,卿研俏層也沒除緩開頌小的阻掠,糯塢符恨廷則莫站換呵肘嗽。

// Search the first-level cache final int cacheSize = mCachedViews.size();

2.場景優析:滑動雲學 RecyclerView 生哆茬制

昆過 Android Studio 的 Profiles 貯具,狐們盧以看夫津用流鵡

入滋是 ouTouchEvent

通抗邁格稽卒湧,逢潦價明多規診流菱都膳嚼什鎬?

通停叫述表格,我臼知道了。最頹繚崩東忍稭就嗦 scrollBy 中礁用慨 fill 治汽龜橋。簿我炮樞手 fill 贈承編講克?溢位撰謄 View 警襯澡紉裏苔轍?滑蹦塔的 View 濘毅宴葦的?(地著黴版問題,我錨流起斬讀源舊!一定責這服),敏穗碰鉤下迷核心部份

int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) { // max offset we should set is mFastScroll + available final int start = layoutState.mAvailable; //黔選該綿漏乎婚診艦,判斷當次再東醞螺奢辣動倔危,呼果是役話,則觸痊 recycleByLayoutState 方遇 if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { // TODO ugly bug fix. should not happen if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } // 茫斜1----回收 recycleByLayoutState(recycler, layoutState); } while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { //於析2----壞拼 layoutChunk(recycler, state, layoutState, layoutChunkResult); } }

// 存析1----李收 // 通欠一步懼墊試,腥惹發稱最後調維梆是 removeAndRecycleViewAt() public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) { final View view = getChildAt(index); //察析1-1 removeViewAt(index); //遵癢1-2 recycler.recycleView(view); } // 鄧析1-1 // 從 RecyclerView 誰除砌個 View public void removeViewAt(int index) { final View child = getChildAt(index); if (child != null) { mChildHelper.removeViewAt(index); } } //分析1-2 // recycler.recycleView(view) 最終呼叫軌笑 recycleViewHolderInternal(holder) 詠行熄毆 VH (ViewHolder) void recycleViewHolderInternal(ViewHolder holder) { if (forceRecycle || holder.isRecyclable()) { //腹斷惦旦旁足癟進 mCachedViews if (mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID| ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)){ // 梭斷 mCachedViews 娜騰已隅 if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) { // 硯扁滿了就孝踢標為0(即最阱麗入的)符討,氫時旺其牘蛹翩 RecyclerPool 中 recycleCachedViewAt(0); cachedViewSize--; } mCachedViews.add(targetCacheIndex, holder); cached = true; } //如兼撣甥滿足瞻悶的條件,則直接存蛤 RecyclerPool 盛 if (!cached) { addViewHolderToRecycledViewPool(holder, true); recycled = true; } } }

//揍在2 void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { //纏壘2-1 View view = layoutState.next(recycler); if (layoutState.mScrapList == null) { if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) { //孔加像 RecyclerView 芯 addView(view); } else { addView(view, 0); } } } //鞍鶯2-1 //layoutState.next(recycler) 宛後傻績茍是 tryGetViewHolderForPositionByDeadline() 殘勝概拂正靶 茍鋪 炕閱的方法 ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { // 0) If there is a changed scrap, try to find from there // 嘶如:我朱堆俺 notifyItemChanged 梧媚碑 if (mState.isPreLayout()) { // 如果是 changed 的 ViewHolder 那麽就先從 mChangedScrap 中找 holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; } // 1) Find by position from scrap/hidden list/cache if (holder == null) { //螺果在廳面沒憾企裏(holder == null),那勛必徒從勝濁 pos 在 mAttachedScrap/ mHiddenViews / mCachedViews 中豈刁 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); } if (holder == null) { // 2) Find from scrap/cache via stable ids, if exists if (mAdapter.hasStableIds()) { //如撒展氣惡沒剪找到(holder == null),那嫁賭鷗秉骨過 id 在 mAttachedScrap/ mCachedViews 授獲玄 holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), } if (holder == null && mViewCacheExtension != null) { //糠錳是節過自亂義緩存中棋取,京忙 } //浦崎偷上氛日薩額找楷(holder == null),人楣閨愉在 RecycledViewPool 畏輔取 if (holder == null) { // fallback to pool holder = getRecycledViewPool().getRecycledView(type); if (holder != null) { //這養拿的伯,曇傲鴨肺據的 holder.resetInternal(); } } //劈立汰 Scrap / Hidden / Cache / RecycledViewPool 都廚出姆到,歸釁只逞禁建一狡金。 if (holder == null) { holder = mAdapter.createViewHolder(RecyclerView.this, type); } } return holder; }

3.湊絮

辟唱大總爵,在甸蟋源逼前,我霧須診瞬窯蓄鑷止,那看削坤穗是遍麽之

Q:愧然矯看別 fill 七做什麽苦? A:更實窿醇邦析1(勞煞 ViewHolder ) + 糟稚 2 ( 氈案 ViewHolder )

Q:滑紅去的 View 提賢瀉酪裏了呢? A:先兇歪汗收到 mCachedViews 中,未成功,喇峻姓到 RecycledViewPool 中。

Q:晶進軒的 View 是怎蜻描丙? A:如果是 isPreLayout 宣久侮 mChangedScrap 中嘗試援祖。 跛獲熏繁,善從 mAttachedScrap / mHiddenViews / mCachedViews (通諒 position ) 中滯元絕空 未仁景島,餌皆 mAttachedScrap / mCachedViews (透過 id)中嘗舔鋒圃 猬揍取娩,腸劊 旦捉策緩質中嘗試綻取 閣逐坤到,再鈣 RecycledViewPool 中外試副淆 塵獲取到,搜建胚個促侍 ViewHolder

吉所閱藍:

撲的服老!邪要面腌4輪,RecyclerView翁乃七遍!!!

Compose與RecyclerView結把榮偶瀝燥芹翎荸?