您的位置:首页 > 其它

andriod listview实现原理以及listview的优化

2016-08-18 15:02 405 查看
1 ListView的实现原理

1.1 Adapter的作用

顾名思义,Adapter是适配器的意思,它在listview和数据源之间起到了一个桥梁的作用,ListView并不会直接和数据源打交道,而是会借助Adapter这个桥梁去访问真正的数据源,与之前不同的是,Adapter的接口是统一的,因此,Listview不用担心适配方面的问题,而Adapter又是一个接口,它可以去实现各种各样的子类,每个子类都能通过自己的逻辑去完成特定的功能,以及与特定数据源的适配操作,如下图:



1.2  RecycleBin机制

还有一个东西我们提前需要了解的,那就是RecycleBin机制,这个机制也是listview能够实现成百上千数据都不会oom最重要的一个原因。

boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {
final int childCount = getChildCount();
if (childCount == 0) {
return true;
}
final int firstTop = getChildAt(0).getTop();
final int lastBottom = getChildAt(childCount - 1).getBottom();
final Rect listPadding = mListPadding;
final int spaceAbove = listPadding.top - firstTop;
final int end = getHeight() - listPadding.bottom;
final int spaceBelow = lastBottom - end;
final int height = getHeight() - getPaddingBottom() - getPaddingTop();
if (deltaY < 0) {
deltaY = Math.max(-(height - 1), deltaY);
} else {
deltaY = Math.min(height - 1, deltaY);
}
if (incrementalDeltaY < 0) {
incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);
} else {
incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);
}
final int firstPosition = mFirstPosition;
if (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) {
// Don't need to move views down if the top of the first position
// is already visible
return true;
}
if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) {
// Don't need to move views up if the bottom of the last position
// is already visible
return true;
}
final boolean down = incrementalDeltaY < 0;
final boolean inTouchMode = isInTouchMode();
if (inTouchMode) {
hideSelector();
}
final int headerViewsCount = getHeaderViewsCount();
final int footerViewsStart = mItemCount - getFooterViewsCount();
int start = 0;
int count = 0;
if (down) {
final int top = listPadding.top - incrementalDeltaY;
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getBottom() >= top) {
break;
} else {
count++;
int position = firstPosition + i;
if (position >= headerViewsCount && position < footerViewsStart) {
mRecycler.addScrapView(child);
}
}
}
} else {
final int bottom = getHeight() - listPadding.bottom - incrementalDeltaY;
for (int i = childCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child.getTop() <= bottom) {
break;
} else {
start = i;
count++;
int position = firstPosition + i;
if (position >= headerViewsCount && position < footerViewsStart) {
mRecycler.addScrapView(child);
}
}
}
}
mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
mBlockLayoutRequests = true;
if (count > 0) {
detachViewsFromParent(start, count);
}
offsetChildrenTopAndBottom(incrementalDeltaY);
if (down) {
mFirstPosition += count;
}
invalidate();
final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
fillGap(down);
}
if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
final int childIndex = mSelectedPosition - mFirstPosition;
if (childIndex >= 0 && childIndex < getChildCount()) {
positionSelector(getChildAt(childIndex));
}
}
mBlockLayoutRequests = false;
invokeOnItemScrollListener();
awakenScrollBars();
return false;
}


当listview向下滑动的时候,就会进入一个for循环当中,从上往下获取子view,如果该子VIew的bottom值小于top值时, 就说明这个子View已经移出屏幕了,所以会调用RecycleBin的addScrapView()方法将这个view加入到废弃缓存当中,并将count计数器加1, 计数器用于记录有多少个VIew被移出了屏幕。那么如果是ListView向上滑动的话,其实过程是基本相同的。接下来会根基计数器的值来进行一个delete操作,它的作用就是把所有移出屏幕的子view全部delete掉,在lixtview的慨念中,所有看不到的view就没有必要为它进行保存。

一旦有任何子View被移出了屏幕,就会将他加入到废弃缓存中,而从obtainVIew()方法中的逻辑来看,一旦有新的数据需要显示到屏幕上,就会尝试从废弃缓存中获取View。所以 它们之间就形成了一个生产者和消费者的模式,那么listVIew神奇的地方就体现出来了,不管你有任意多条数据需要显示,ListView的子View其实来来回回就那么几个,移出屏幕的子View会很快被移人屏幕的数据重新利用起来,因而不管我们加载多少数据都不会出现oom的情况,甚至内存都不会有所增加。

我们平时写的getView()方法要判断一下convertView是不是等于null,如果等于null才调用inflate()方法加载布局,不等于null就可以直接利用convertView,因为convertView就是我们之前利用过的View,只不过被移出屏幕后进入到废弃缓存中,现在又重新拿出来使用而已,然后我们只需要把convertView中的数据更新成当前位置上应该显示的数据,那么看起来就像是全新加载出来的一个布局一样。

2  listView的优化方法

(1)ViewHolder Tag必不可少。

(2)如果自定义Item中有设计到图片等,一定要狠狠的处理图片,图片占的内存是listview项中最恶心的。处理图片有一下几种:

1,不要直接拿个路径就去循环decodeFile(),用option保存图片大小,不要加载图片到内存中

2,拿到的图片一定要经过边界压缩

3,在listVIew中取图片时也不要直接拿个路径去取图片,而是以WeakReference(使用弱引用代替强引用来存储图片信息,而不是图片)

4,在getview中做图片转换时,产生的中间变量一定及时释放。

(3)尽量避免在BaseAdapter中使用static来定义全局变量,因为用static修饰的变量,它的生命周期很长的,如果用它来引用一些资源耗费过多的实例(比如Context的情况最多),这时就要尽量避免使用了。

(4)如果为了满足需求下必须使用Context的话,Context尽量使用ApplicationContext, 因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

(5)尽量避免在listview适配器中使用线程,因为线程产生内存泄漏的主要原因在于生命周期的不可控制。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐