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

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适配器中使用线程,因为线程产生内存泄漏的主要原因在于生命周期的不可控制。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值