mChildHelper.detachViewFromParent(index);
}
}
}
// RecyclerView 子表项管理类
class ChildHelper {
// 将指定位置的表项从 RecyclerView detach
void detachViewFromParent(int index) {
final int offset = getOffset(index);
mBucket.remove(offset);
// 最终实现 detach 操作的回调
mCallback.detachViewFromParent(offset);
}
}
LayoutManager
会将 detach 任务委托给ChildHelper
,ChildHelper
再执行detachViewFromParent()
回调,它在初始化ChildHelper
时被实现:
public class RecyclerView {
// 初始化 ChildHelper
private void initChildrenHelper() {
// 构建 ChildHelper 实例
mChildHelper = new ChildHelper(new ChildHelper.Callback() {
@Override
public void detachViewFromParent(int offset) {
final View view = getChildAt(offset);
…
// 调用 ViewGroup.detachViewFromParent()
RecyclerView.this.detachViewFromParent(offset);
}
…
}
}
}
RecyclerView
detach 表项的最后一步调用了ViewGroup.detachViewFromParent()
:
public abstract class ViewGroup {
// detach 子控件
protected void detachViewFromParent(int index) {
removeFromArray(index);
}
// 删除子控件的最后一步
private void removeFromArray(int index) {
final View[] children = mChildren;
// 将子控件持有的父控件引用置空
if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
children[index].mParent = null;
}
final int count = mChildrenCount;
// 将父控件持有的子控件引用置空
if (index == count - 1) {
children[–mChildrenCount] = null;
} else if (index >= 0 && index < count) {
System.arraycopy(children, index + 1, children, index, count - index - 1);
children[–mChildrenCount] = null;
}
…
}
}
ViewGroup.removeFromArray()
是容器控件移除子控件的最后一步(ViewGroup.removeView()
也会调用这个方法)。
至此可以得出结论:
在每次向
RecyclerView
填充表项之前都会先清空现存表项。
目前看来,detach view
和remove view
差不多,它们都会将子控件从父控件的孩子列表中删除,唯一的区别是detach
更轻量,不会触发重绘。而且detach
是短暂的,被detach
的 View 最终必须被彻底 remove 或者重新 attach。(下面就会马上把他们重新 attach)
scrap 表项
scrap 表项的意思是回收表项并将其存入mAttachedScrap
列表,它是回收器Recycler
中的成员变量:
public class RecyclerView {
public final class Recycler {
// scrap 列表
final ArrayList mAttachedScrap = new ArrayList<>();
}
}
mAttachedScrap
是一个 ArrayList 结构,用于存储ViewHolder
实例。
RecyclerView 填充表项前,除了会 detach 所有可见表项外,还会同时 scrap 它们:
public class RecyclerView {
public abstract static class LayoutManager {
// 回收表项
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
…
// detach 表项
detachViewAt(index);
// scrap 表项
recycler.scrapView(view);
…
}
}
}
scrapView()
是回收器Recycler
的方法,正是这个方法将表项回收到了mAttachedScrap
列表中:
public class RecyclerView {
public final class Recycler {
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
// 表项不需要更新,或被移除,或者表项索引无效时,将被会收到 mAttachedScrap
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
holder.setScrapContainer(this, false);
// 将表项回收到 mAttachedScrap 结构中
mAttachedScrap.add(holder);
} else {
// 只有当表项没有被移除且有效且需要更新时才会被回收到 mChangedScrap
if (mChangedScrap == null) {
mChangedScrap = new ArrayList();
}
holder.setScrapContainer(this, true);
mChangedScrap.add(holder);
}
}
}
}
scrapView()
中根据ViewHolder
状态将其会收到不同的结构中,同样地,硬看源码很难快速判断执行了那个分支,继续断点调试,Demo 场景中所有的表项都会被回收到mAttachedScrap
结构中。(关于 mAttachedScrap 和 mChangedScrap 的区别会在后续文章分析)
分析至此,进一步细化刚才得到的结论:
在每次向
RecyclerView
填充表项之前都会先清空 LayoutManager 中现存表项,将它们 detach 并同时缓存入mAttachedScrap
列表中。
将结论应用在 Demo 的场景,即是:RecyclerView 在预布局阶段准备向列表中填充表项前,会清空现有的表项 1、2,把它们都 detach 并回收对应的 ViewHolder 到 mAttachedScrap
列表中。
从缓存拿填充表项
预布局与 scrap 缓存的关系
缓存定是为了复用,啥时候用呢?紧接着的“填充表项”中就立马会用到:
public class LinearLayoutManager {
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
…
// detach 表项
detachAndScrapAttachedViews(recycler);
…
// 填充表项
fill()
}
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
// 计算剩余空间
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
// 不停的往列表中填充表项,直到没有剩余空间
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
// 填充单个表项
layoutChunk(recycler, state, layoutState, layoutChunkResult);
…
}
}
// 填充单个表项
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
// 获取下一个被填充的视图
View view = layoutState.next(recycler);
…
// 填充视图
addView(view);
…
}
}
填充表项时,通过layoutState.next(recycler)
获取下一个该被填充的表项视图:
public class LinearLayoutManager {
static class LayoutState {
View next(RecyclerView.Recycler recycler) {
…
// 委托 Recycler 获取下一个该填充的表项
final View view = recycler.getViewForPosition(mCurrentPosition);
…
return view;
}
}
}
public class RecyclerView {
public final class Recycler {
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
}
View getViewForPosition(int position, boolean dryRun) {
// 调用链最终传递到 tryGetViewHolderForPositionByDeadline()
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
}
沿着调用链一直往下,最终走到了Recycler.tryGetViewHolderForPositionByDeadline()
,在RecyclerView缓存机制(咋复用?)中对其做过详细介绍,援引结论如下:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
关于面试的充分准备
一些基础知识和理论肯定是要背的,要理解的背,用自己的语言总结一下背下来。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,我能明显感觉到国庆后多了很多高级职位,所以努力让自己成为高级工程师才是最重要的。
好了,希望对大家有所帮助。
接下来是整理的一些Android学习资料,有兴趣的朋友们可以关注下我免费领取方式。
①Android开发核心知识点笔记
②对标“阿里 P7” 40W+年薪企业资深架构师成长学习路线图
③面试精品集锦汇总
④全套体系化高级架构视频
**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算