下面将会系统性分析Android中ListView的生命周期与内部机制。重点参考了AbsListView和RecycleBin源码,从以下方面展开说明:
-
初始化阶段:ListView在创建时的布局测量、子视图生成逻辑。
-
数据绑定机制:Adapter数据与视图的绑定过程及相关回调方法。
-
更新流程:数据变化后ListView如何刷新、重绘与重用子视图。
-
回收与复用机制:RecycleBin在视图缓存与复用中的作用、回收策略与效率优化。
-
释放与销毁:ListView在滑动或销毁过程中的资源管理与内存释放方法。
一、初始化阶段:创建、测量与子视图生成
1. 类层级与职责划分
View
└── ViewGroup
└── AdapterView
└── AbsListView
└── ListView
-
AdapterView:定义 Adapter 数据驱动视图的抽象行为
-
AbsListView:实现滚动、复用、选择、回收等核心机制
-
ListView:实现线性垂直布局(一个接一个)
2. 构造与初始化流程
在 AbsListView 构造函数中完成核心初始化:
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr)
关键初始化内容:
-
创建 RecycleBin mRecycler
-
初始化滚动相关对象(Scroller / OverScroller)
-
设置 CacheHint、FadingEdge、Selector
-
初始化触摸、速度追踪器等
✅ 设计思想:
在构造阶段完成“可复用基础设施”的搭建,而不是创建任何 Item View。
3. 测量与布局入口
测量阶段
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
-
AbsListView 并 不会在 onMeasure 中创建所有子 View
-
只确定自身尺寸(match_parent / wrap_content)
-
Item 的测量延后到布局阶段
布局阶段
protected void onLayout(boolean changed, int l, int t, int r, int b)
内部会调用:
layoutChildren();
这是 ListView 真正开始创建 / 复用子 View 的地方。
4. layoutChildren 核心流程(简化)
void layoutChildren() {
// 1. 将当前子 View 移交给 RecycleBin
mRecycler.fillActiveViews(childCount, mFirstPosition);
detachAllViewsFromParent();
// 2. 根据 mFirstPosition 向下填充可见区域
fillFromTop();
// 3. 将未复用的 ActiveViews 转入 ScrapViews
mRecycler.scrapActiveViews();
}
✅ 关键点:
-
不会一次性创建所有 Item
-
只填充“屏幕可见 + 少量缓存”的 View
二、数据绑定机制:Adapter → View
1. Adapter 绑定入口
setAdapter(ListAdapter adapter)
内部流程:
-
注册
DataSetObserver -
清空旧 View
-
清空 RecycleBin
-
触发
requestLayout()
2. 获取 Item View 的统一入口
View obtainView(int position, boolean[] isScrap)
执行逻辑(核心):
-
从 TransientState View 获取(动画、临时状态)
-
从 RecycleBin.getScrapView(position) 获取
-
若没有可用缓存 → 调用
adapter.getView()View child = mAdapter.getView(position, convertView, this);
✅ getView 的核心职责:
-
将 position 对应的数据绑定到 View
-
尽量复用
convertView
3. ViewType 与复用安全
int getItemViewType(int position)
int getViewTypeCount()
-
RecycleBin 按 ViewType 分组缓存
-
避免不同布局错误复用
✅ 性能关键点:
ViewType 决定了缓存池的隔离边界
三、更新流程:数据变化后的刷新机制
1. 数据变更触发
adapter.notifyDataSetChanged();
→ DataSetObserver.onChanged()
2. AbsListView 响应数据变化
handleDataChanged();
requestLayout();
invalidate();
-
mDataChanged = true -
下一次 layoutChildren 时:
-
不复用 ActiveViews
-
全部子 View 进入 Scrap
-
重新从 Adapter 绑定数据
-
3. 局部 vs 全量刷新
ListView 不支持精细化刷新(与 RecyclerView 的区别):
-
notifyDataSetChanged → 几乎重走 layout
-
但 View 对象仍可复用
✅ 优化建议:
-
使用稳定 ID:
hasStableIds() -
减少 notify 调用频率
四、回收与复用机制:RecycleBin 深度解析
1. RecycleBin 的两级缓存结构
class RecycleBin {
View[] mActiveViews;
ArrayList<View>[] mScrapViews;
}
缓存级别
含义
ActiveViews
本次 layout 前屏幕上可见的 View
ScrapViews
已离屏、可复用的 View(按 ViewType)
2. 回收流程
滚动时
addScrapView(child, position)
触发条件:
-
View 完全离开可见区域
-
数据未发生变化
layout 结束
scrapActiveViews()
将未复用的 ActiveViews 移入 ScrapViews
3. 获取缓存 View
View getScrapView(int position)
策略:
-
优先找 相同 position
-
再取 同 ViewType 的最后一个
-
找不到返回 null
✅ 设计目标:
在正确性前提下,最大化 View 复用概率
4. 性能优化设计
-
O(1) 复用路径
-
按 ViewType 分桶
-
避免重复 inflate
-
ViewHolder 模式减少 findViewById
五、释放与销毁:资源管理与内存安全
1. 滑动过程中的释放
-
离屏 View → addScrapView
-
清理临时状态:
child.clearAccessibilityFocus(); child.onStartTemporaryDetach();
2. Adapter 更换 / ListView 销毁
setAdapter(null);
内部执行:
-
注销 DataSetObserver
-
mRecycler.clear() -
removeAllViewsInLayout()
3. RecycleBin.clear()
public void clear() {
for (ArrayList<View> scrap : mScrapViews) {
scrap.clear();
}
}
✅ 效果:
-
解除 View 引用
-
防止 Context / Activity 泄漏
-
交由 GC 回收
六、整体性能设计总结
ListView 的核心设计哲学可以概括为:
用最少的 View,承载无限的数据
关键手段包括:
-
延迟创建(按需填充)
-
两级缓存(Active + Scrap)
-
ViewType 隔离
-
Adapter 驱动 + Observer 监听
-
滚动过程中只做“位移 + 少量增删”
七、对比性总结(一句话)
ListView 是一个以 AbsListView + RecycleBin 为核心、通过 View 复用来实现高效滚动的大数据列表容器,其性能上限高度依赖 Adapter 的实现质量。