android进阶篇02、RecyclerView回收复用机制源码解析

public void recycleView(@NonNull View view) {

、、、

recycleViewHolderInternal(holder); //1

}

6-1 RecyclerView # Recycler

在分析recycleViewHolderInternal方法之前,我们先简单分析一下RecyclerView的两个内部类Recycler类和RecyclerViewPool;

RecyclerView # Recycler

Recycler如下所示,注释1和注释2是一级缓存,其实就是泛型为ViewHolder的ArrayList;注释3就是二级缓存,注释4处的mViewCacheMax表示二级缓存的最大容量为2;注释5就是四级缓存RecycledViewPool;注释6就是第三级缓存,也就是自定义缓存;

public final class Recycler {

final ArrayList mAttachedScrap = new ArrayList<>(); //1

ArrayList mChangedScrap = null; //2

final ArrayList mCachedViews = new ArrayList(); //3

private final List

mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);

private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;

int mViewCacheMax = DEFAULT_CACHE_SIZE; //4

RecycledViewPool mRecyclerPool; //5

private ViewCacheExtension mViewCacheExtension; //6

static final int DEFAULT_CACHE_SIZE = 2; //7

、、、

}

RecyclerView # RecycledViewPool

RecycledViewPool如下所示,先看注释2处的内部类ScrapData,注释3处的mScrapHeap就是缓存ViewHolder的ArrayList,注释4处表示最大容量为5,即可以缓存ViewHolder的个数为5;注释5处mScrap是一个SparseArray,其泛型为内部类ScrapData,这表示什么意思呢?就是说RecyclerViewPool对每种类型的ViewHolder最大缓存个数为5,然后可以缓存多种ViewHolder类型;

public static class RecycledViewPool {

private static final int DEFAULT_MAX_SCRAP = 5; //1

static class ScrapData { //2

final ArrayList mScrapHeap = new ArrayList<>(); //3

int mMaxScrap = DEFAULT_MAX_SCRAP; //4

long mCreateRunningAverageNs = 0;

long mBindRunningAverageNs = 0;

}

SparseArray mScrap = new SparseArray<>(); //5

、、、

}

6-2 Recycler -> recycleViewHolderInternal

在recycleViewHolderInternal方法中才是执行真正的回收操作,主要是使用第二级缓存mCachedViews和第四级缓存RecyclerViewPool;注释1、2处表示mCachedViews容量满了之后会调用recycleCachedViewAt方法,然后将size–;注释3处表示往mCachedViews添加;注释4表示往缓存池添加;

void recycleViewHolderInternal(ViewHolder holder) {

、、、

if (forceRecycle || holder.isRecyclable()) {

if (mViewCacheMax > 0

&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID

| ViewHolder.FLAG_REMOVED

| ViewHolder.FLAG_UPDATE

| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {

// Retire oldest cached view

int cachedViewSize = mCachedViews.size();

if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {

recycleCachedViewAt(0); //1

cachedViewSize–; //2

}

、、、

mCachedViews.add(targetCacheIndex, holder); //3

cached = true;

}

if (!cached) {

addViewHolderToRecycledViewPool(holder, true); //4

recycled = true;

}

}

}

7、Recycler -> recycleCachedViewAt

recycleCachedViewAt如下所示,注释1、2表示将mCachedViews的viewHolder移除,然后添加到RecycledViewPool;

void recycleCachedViewAt(int cachedViewIndex) {

if (DEBUG) {

Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);

}

ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);

if (DEBUG) {

Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);

}

addViewHolderToRecycledViewPool(viewHolder, true); //1

mCachedViews.remove(cachedViewIndex); //2

}

8、Recycler -> addViewHolderToRecycledViewPool

如下所示,注释1表示将ViewHolder添加到RecyclerViewPool;到此回收完毕;

void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {

、、、

getRecycledViewPool().putRecycledView(holder); //1

}

三、复用部分


1、LinearLayoutManager -> layoutChunk

接着第一部分的layoutChunk方法分析复用部分,如下所示,注释1表示又会调用LayoutState的next方法;

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,

LayoutState layoutState, LayoutChunkResult result) {

View view = layoutState.next(recycler); //1

、、、

RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();

if (layoutState.mScrapList == null) {

if (mShouldReverseLayout == (layoutState.mLayoutDirection

== LayoutState.LAYOUT_START)) {

addView(view);

} else {

addView(view, 0);

}

}

、、、

}

2、LayoutState -> next

next方法如下所示,注释1又调用了getViewForPosition方法;

View next(RecyclerView.Recycler recycler) {

if (mScrapList != null) {

return nextViewFromScrapList();

}

final View view = recycler.getViewForPosition(mCurrentPosition); //1

mCurrentPosition += mItemDirection;

return view;

}

3、Recycler -> getViewForPosition

getViewForPosition方法如下所示,注释1又会调用到注释2;

public View getViewForPosition(int position) {

return getViewForPosition(position, false); //1

}

View getViewForPosition(int position, boolean dryRun) {

return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView; //2

}

4、Recycler -> tryGetViewHolderForPositionByDeadline

tryGetViewHolderForPositionByDeadline方法就是真正去寻找缓存进行复用了,这个方法比较长,这里只截取主要部分的代码,如下所示;

注释1处表示从mChangedScrap中寻找;

注释2和注释3表示从mAttachedScrap和mCachedView中去寻找;

注释4表示从自定义缓存中去寻找;

注释5表示从RecyclerViewPool中去寻找;

注释6表示如果上边所有的缓存都没有的话,就调用我们重写的Adapter的onCreateViewHolder去创建;

注释7处表示获取到ViewHolder之后再去调用Adapter的onBindViewHolder进行数据绑定,所以现在我们应该明白为什么要在我们重写的Adapter中重写这两个方法了;

ViewHolder tryGetViewHolderForPositionByDeadline(int position,

boolean dryRun, long deadlineNs) {

、、、

ViewHolder holder = null;

// 0) If there is a changed scrap, try to find from there

if (mState.isPreLayout()) {

holder = getChangedScrapViewForPosition(position); //1

fromScrapOrHiddenOrCache = holder != null;

}

// 1) Find by position from scrap/hidden list/cache

if (holder == null) {

holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); //2

、、、

}

if (holder == null) {

、、、

// 2) Find from scrap/cache via stable ids, if exists

if (mAdapter.hasStableIds()) {

holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),

type, dryRun); //3

}

if (holder == null && mViewCacheExtension != null) {

// We are NOT sending the offsetPosition because LayoutManager does not

// know it.

final View view = mViewCacheExtension

.getViewForPositionAndType(this, position, type); //4

、、、

}

if (holder == null) { // fallback to pool

holder = getRecycledViewPool().getRecycledView(type); //5

、、、

}

if (holder == null) {

、、、

holder = mAdapter.createViewHolder(RecyclerView.this, type); //6

、、、

}

}

、、、

boolean bound = false;

if (mState.isPreLayout() && holder.isBound()) {

// do not update unless we absolutely have to.

holder.mPreLayoutPosition = position;

} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {

、、、

bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);//7

}

、、、

return holder;

}

四、onLayout入口


1、RecyclerView -> onLayout

前面三个部分我们是从onTouchEvent方法的MOVE分支中切入讨论的,这一部分我们从RecyclerView的布局方法onLayout方法切入分析;

protected void onLayout(boolean changed, int l, int t, int r, int b) {

TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);

dispatchLayout(); //1

TraceCompat.endSection();

mFirstLayoutComplete = true;

}

2、RecyclerView -> dispatchLayout

如下所示,可以看到RecyclerView的dispatchLayout方法中主要分为三步;这里分析一下注释2的第二步;

void dispatchLayout() {

、、、

mState.mIsMeasuring = false;

if (mState.mLayoutStep == State.STEP_START) {

dispatchLayoutStep1(); //1

mLayout.setExactMeasureSpecsFrom(this);

dispatchLayoutStep2(); //2

} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()

|| mLayout.getHeight() != getHeight()) {

// First 2 steps are done in onMeasure but looks like we have to run again due to

// changed size.

mLayout.setExactMeasureSpecsFrom(this);

dispatchLayoutStep2();

} else {

// always make sure we sync them (to ensure mode is exact)

mLayout.setExactMeasureSpecsFrom(this);

}

dispatchLayoutStep3(); //3

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

文末

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值