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最重要的一个原因。
当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适配器中使用线程,因为线程产生内存泄漏的主要原因在于生命周期的不可控制。
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适配器中使用线程,因为线程产生内存泄漏的主要原因在于生命周期的不可控制。
相关文章推荐
- Android实训案例(五)——四大组件之一ContentProvider的使用,通讯录的实现以及ListView的优化
- ListView中子view复用机制的实现原理以及图片错位的解决方案
- 仿今日头条下拉出现SearchBar,再下拉刷新效果,SearchListView实现以及原理讲解
- SDWebImage的内部原理、实现机制以及优化方式
- listView的原理以及优化方式以及View的绘制流程
- Android实训案例(五)——四大组件之一ContentProvider的使用,通讯录的实现以及ListView的优化
- 前端性能优化之 —— 图片延迟加载 (原理以及实现方式)
- 前端性能优化之 —— 图片延迟加载 (原理以及实现方式)
- 数据库索引实现原理以及SQL优化技巧
- 前端性能优化之 —— 图片延迟加载 (原理以及实现方式)
- 前端性能优化之 —— 图片延迟加载 (原理以及实现方式)
- 仿今日头条下拉出现SearchBar,再下拉刷新效果,SearchListView实现以及原理讲解
- 仿今日头条下拉出现SearchBar,再下拉刷新效果,SearchListView实现以及原理讲解
- Android实训案例(五)——四大组件之一ContentProvider的使用,通讯录的实现以及ListView的优化
- 前端性能优化之 —— 图片延迟加载 (原理以及实现方式)
- HTTP PUSH技术原理,结合ASP.NET实现以及评述
- 讨论 winform 引擎以及 CancelButton,OKButton 的实现原理[转载]
- HTTP PUSH技术原理,结合ASP.NET实现以及评述
- [转载]蚁群算法ACO(ant colony optimization)的原理以及实现源代码
- 智能指针设计原理以及实现