0
回答
解决 ListView 滚动卡的问题
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

写了个类似下面的GridView,滚动的时候有卡或者跳格的现象,尤其当记录比较多的时候。

GridView和ListView机制原理是类似的,都是基于ListAdapter来处理View的控制的。在排查问题的时候也测试了用ListView替换GridView,问题依旧。

实现的示例大致是这个样子:

imageimage

测试数据有600条左右。不过,即使减到40条左右,也是会卡的。在ddms log中监控dalvik日志,会有大量下面的信息:

01-14 10:37:47.579: DEBUG/dalvikvm(13626): GC_EXTERNAL_ALLOC freed 58 objects / 2072 bytes in 37ms

基本上每滚动一次,就会出10多条。这是造成卡的主要原因。也就是说,要释放大量的临时对象。

这些临时对象还不是我自己创建的对象,我把结果集合已经一次性加载到内存中了。也不是图片显示造成的,我把图片去掉现象一样。

另外,也考虑了比如扩大堆内存的方法,但是不对症下药,因为并不是内存达到堆的上限造成full gc。有关堆的日志可以过滤dalvikvm-heap看到。如果堆内存full gc会有类似这样的日志:

dalvikvm-heap(11651): Heap Massage needed (768000-byte external allocation too big)
dalvikvm-heap(11651): Full GC (don’t collect SoftReferences)

可以初步判断,是GridView/ListView为实现滚屏产生的瞬态使用对象,随着滚动而被快速丢弃了,需要垃圾回收,造成性能问题。

可是,为什么Android自带的通讯录却不卡呢?我的手机上的通讯录也有将近300条的记录。通过ddms日志观察,没有频繁的GC_EXTERNAL_ALLOC日志,从头滚动到尾只出现了1-2次。

于是决定查看通讯录的源代码。这几行:

@Override 
public View getView(int position, View convertView, ViewGroup parent) {

……

View v; 
if (convertView == null || convertView.getTag() == null) { 
    newView = true; 
    v = newView(mContext, cursor, parent); 
} else { 
    newView = false; 
    v = convertView; 
}

说明了对ListView元素视图的复用。再查api:

convertView    The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.

因此,可以认为,convertView是元素的缓存,因为元素本身没有变化,因此可以判断如果非空,就可以复用。比如我示例中是这样写的:

if (convertView == null) { 
    convertView=activity.getLayoutInflater().inflate( 
            R.layout.album_grid_element, parent, false); 
}

之前是每次直接创建新的view的,没有考虑复用convertView。这是造成卡的根本原因。

这次的另外的一个教训是,解决问题没有从api层面层层深入解决问题,即这样的次序:api是否能解决?是否需要通过自己的优化解决?是否需要系统的部署方面的配置比如堆的调整来解决等。

结果是走了弯路,消耗了时间。

以后还是要先通读api文档。这次出问题,是建立在以前随手写的简单示例基础上的。

文章转自:http://marshal.easymorse.com/archives/3944

举报
无鸯
发帖于6年前 0回/1K+阅
顶部