AbsListView中item重用机制
AbsListView中item重用机制
ListView是一个非常神奇的功能,我相信大家应该都体验过,即使在ListView中加载非常非常多的数据,比如达到成百上千条甚至更多,ListView都不会发生OOM或者崩溃,而且随着我们手指滑动来浏览更多数据时,程序所占用的内存竟然都不会跟着增长。要达到这种效果,一定是重用了item,将划出屏幕的item作为新item,添加到屏幕中,从而达到了内存稳定的效果。
原理图
RecycleBin机制
RecycleBin为AbsListView中的一个内部类。主要作用就是实现Item的缓存,将废弃的item添加到缓存中,为以后出现的item重用,使得ListView能够实现成百上千条数据都不会OOM最重要的一个原因。
核心方法介绍
class RecycleBin {
/**
* 当前有效的item的View
*/
private View[] mActiveViews = new View[0];
/**
* 被废弃的item的View
*/
private ArrayList<View>[] mScrapViews;
/**
* RecycleBin当中使用mActiveViews这个数组来存储View,调用这个方法后就会根据传入的参数来将ListView中的指定元素
* 存储到mActiveViews数组当中。
* @param childCount 表示要存储的view的数量
* @param firstActivePosition AbsListView中第一个可见元素的position值
*/
void fillActiveViews(int childCount, int firstActivePosition);
/**
* 与fillActiveViews()是对应的,用于从mActiveViews数组当中获取数据。需要注意的是,mActiveViews当中所存储的
* View,一旦被获取了之后就会从mActiveViews当中移除,下次获取同样位置的View将会返回null,也就是说mActiveViews
* 不能被重复利用。
* @param position 元素在ListView当中的位置
* @return 返回当前显示的item
*/
View getActiveView(int position);
/**
* 用于将一个废弃的View进行缓存,该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕),
* 就应该调用这个方法来对View进行缓存
* @param scrap 被废弃的View
*/
void addScrapView(View scrap);
/**
* 用于从废弃缓存中取出一个View
* @param position 位置
* @return 废弃缓存中的View
*/
View getScrapView(int position);
}
/**
* 当前有效的item的View
*/
private View[] mActiveViews = new View[0];
/**
* 被废弃的item的View
*/
private ArrayList<View>[] mScrapViews;
/**
* RecycleBin当中使用mActiveViews这个数组来存储View,调用这个方法后就会根据传入的参数来将ListView中的指定元素
* 存储到mActiveViews数组当中。
* @param childCount 表示要存储的view的数量
* @param firstActivePosition AbsListView中第一个可见元素的position值
*/
void fillActiveViews(int childCount, int firstActivePosition);
/**
* 与fillActiveViews()是对应的,用于从mActiveViews数组当中获取数据。需要注意的是,mActiveViews当中所存储的
* View,一旦被获取了之后就会从mActiveViews当中移除,下次获取同样位置的View将会返回null,也就是说mActiveViews
* 不能被重复利用。
* @param position 元素在ListView当中的位置
* @return 返回当前显示的item
*/
View getActiveView(int position);
/**
* 用于将一个废弃的View进行缓存,该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕),
* 就应该调用这个方法来对View进行缓存
* @param scrap 被废弃的View
*/
void addScrapView(View scrap);
/**
* 用于从废弃缓存中取出一个View
* @param position 位置
* @return 废弃缓存中的View
*/
View getScrapView(int position);
}
AbsListView回收过程分析
分析回收过程
主要分三步,来讲解AbsListView中item的回收,如下:
1. 首次调用Layout,主要分析创建item的过程
2. 再次调用Layout,主要分析将item回收到mActiviyView缓存中,并重用过程
3.AbsListView滑动分析,主要分析将item的回收到scrapview中并重用过程
首次调用Layout
函数调用流程
onLayout() AbsListView中统一实现布局方法,并且调用子类layoutChildren()
—>layoutChildren() ListView与GridView中各自实现视图布局,这里以ListView为例—>RecycleBin.fillActiveViews() 调用此方法是为了将ListView的子View进行缓存的,但当前ListView为空,因此这一行暂时还起不了任何作用
—>fillFromTop() 直接调用fillDown()方法,没有做什么操作
—>fillDown() 内部while循环构造item,while循环单单刷新屏幕可见部分。构造item,使用makeAndAddView()方法
—>makeAndAddView() 由于当前RecycleBin.mActiveViews为空,所以调用obtainView声明item
—>obtainView() 由于当前RecycleBin.mScrapViews为空,所以调用Adapter.getView()构建item。
—>Adapter.getView() 适配器获取item
此次调用,主要完成了AbsListView可见部分的视图创建。RecycleBin的缓存操作都还没有来得及实现。
再次调用Layout
函数调用流程
onLayout() AbsListView中统一实现布局方法调用,并且调用子类layoutChildren()
—>layoutChildren() ListView与GridView中各自实现视图布局,这里以ListView为例
—>RecycleBin.fillActiveViews() 首次调用Layout时,已经创建了item,调用此方法将当前item缓存到RecycleBin.mActiveViews中
—>fillSpecific() 此函数主要负责填充ListView中的视图,并且调用fillDown()与fillUp()继续填充,最终还是调用makeAndAddView()方法
—>makeAndAddView() 当前RecycleBin.mActiveViews已经在layoutChildren()中填充了,所以可以直接调用RecycleBin.getActivityView()获取,并设置位置
onLayout() AbsListView中统一实现布局方法调用,并且调用子类layoutChildren()
—>layoutChildren() ListView与GridView中各自实现视图布局,这里以ListView为例
—>RecycleBin.fillActiveViews() 首次调用Layout时,已经创建了item,调用此方法将当前item缓存到RecycleBin.mActiveViews中
—>fillSpecific() 此函数主要负责填充ListView中的视图,并且调用fillDown()与fillUp()继续填充,最终还是调用makeAndAddView()方法
—>makeAndAddView() 当前RecycleBin.mActiveViews已经在layoutChildren()中填充了,所以可以直接调用RecycleBin.getActivityView()获取,并设置位置
此次调用,主要展示了AbsListView可见部分的视图重用。RecycleBin中的RecycleBin.mActiveViews已经构建完成了,所以可以调用RecycleBin.getActivityView()获取缓存,并复用。
AbsListView滑动分析
onTouchEvent() 触摸事件回调接口,判断当前触摸类型,分发事件。这里主要是滑动分析,所有主要看onTouchMove()方法
—>onTouchMove() 主要是对滑动事件的逻辑处理,最终调用scrollIfNeeded()方法进行滑动处理
—>scrollIfNeeded() 处理滚动操作,最终调用trackMotionScroll()方法
—>onTouchMove() 主要是对滑动事件的逻辑处理,最终调用scrollIfNeeded()方法进行滑动处理
—>scrollIfNeeded() 处理滚动操作,最终调用trackMotionScroll()方法
—>trackMotionScroll() 此方法做了最关键的操作,就是将已经滑出屏幕的item,添加到了RecycleBin.mScrapViews中,方便复用。并调用fillGap()填充布局
—>fillGap() 此方法为滑动后空出来的位置填充item视图,最终调用makeAndAddView()方法获取item
—>makeAndAddView() 由于RecycleBin.mActiveViews中不存在新位置的item的缓存,所以调用obtainView声明item
—>obtainView() 由于trackMotionScroll()中将移出的item填充到了RecycleBin.mScrapViews缓存中,所以复用RecycleBin.mScrapViews中的item来填充新item此次调用,主要展示了AbsListView中真正的item的视图重用。将从屏幕移除的item添加到RecycleBin.mScrapViews中,再从RecycleBin.mScrapViews中调用废弃的item并复用。
参考