当前位置: 华文问答 > 数码

真的厚难理档?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结把荣偶沥燥芹翎荸?