很多垃圾收集在一个列表视图

我有一个使用自定义适配器的ListView。 自定义适配器的getView使用所有build议的做法:

@Override public View getView(int position, View convertView, ViewGroup parent) { SuscriptionsViewsHolder holder; ItemInRootList item = mItemsInList.get(position); if (convertView == null) { convertView = mInflater.inflate(R.layout.label, null); holder = new SuscriptionsViewsHolder(); holder.label = (TextView) convertView.findViewById(R.id.label_label); holder.icon = (ImageView) convertView.findViewById(R.id.label_icon); convertView.setTag(holder); } else { holder = (SuscriptionsViewsHolder) convertView.getTag(); } String text = String.format("%1$s (%2$s)", item.title, item.unreadCount); holder.label.setText(text); holder.icon.setImageResource(item.isLabel ? R.drawable.folder : R.drawable.file ); return convertView; } 

但是,当我滚动时,由于垃圾收集严重,

 GC_EXTERNAL_ALLOC freed 87K, 48% free 2873K/5447K, external 516K/519K, paused 30ms GC_EXTERNAL_ALLOC freed 7K, 48% free 2866K/5447K, external 1056K/1208K, paused 29ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2866K/5447K, external 1416K/1568K, paused 28ms GC_EXTERNAL_ALLOC freed 5K, 48% free 2865K/5447K, external 1600K/1748K, paused 27ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2865K/5447K, external 1780K/1932K, paused 30ms GC_EXTERNAL_ALLOC freed 2K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms GC_EXTERNAL_ALLOC freed 2K, 48% free 2870K/5447K, external 1780K/1932K, paused 25ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms GC_EXTERNAL_ALLOC freed 3K, 48% free 2870K/5447K, external 1780K/1932K, paused 25ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2871K/5447K, external 1780K/1932K, paused 28ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2871K/5447K, external 1780K/1932K, paused 26ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 27ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 34ms 

什么似乎是错的?

编辑@ 12:47 GMT:

实际上它比这个稍微复杂一些。 我的应用程序用户界面基于2部分。 一个是屏幕的大脑,创build的意见,处理用户input等。另一个是一个Fragment如果该设备有Android 3.0,否则它是一个Activity

GC发生在我的Nexus One 2.3.3设备上,所以使用Activity 。 我没有和我一起testing一个Fragment的行为。

如果需要,我可以发布源代码,但让我试着解释一下:

  • RootList是UI的大脑。 它包含 :
    • 将被放置在ListView的项目的List<>
    • 一个从SQLite数据库build立这个列表的方法
    • 一个基本上只包含上面粘贴的getView方法的自定义BaseAdapter
  • RootListActivity是一个ListActivity ,它:
    • 使用XML布局
    • 该布局当然是一个id为android.id.listlistview
    • Activitycallback被转发到RootList类,使用创build活动时创build的RootList实例(构造函数,而不是onCreate
    • onCreate ,我调用RootList的方法来创build项目列表,并将列表数据设置为从BaseAdapter派生的自定义类的新实例

编辑于5月17日下午9:36 GMT:

这里是活动的代码和做这件事的类。 http://pastebin.com/EgHKRr4r

我发现这个问题。 我的活动的XML布局是:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <include android:id="@+id/rootlist_header" layout="@layout/pre_honeycomb_action_bar" /> <ListView android:id="@android:id/list" android:layout_below="@id/rootlist_header" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:textColor="#444444" android:divider="@drawable/list_divider" android:dividerHeight="1px" android:cacheColorHint="#00000000" /> </RelativeLayout> 

如果我删除了android:cacheColorHint="#00000000" ,那么重GC就出来了,滚动就顺利了 ! 🙂

我不知道为什么设置这个参数,因为我不需要它。 也许我拷贝太多,而不是实际构build我的XML布局。

感谢您的支持,我真的很感谢您的帮助。

我也有一个问题的android:cacheColorHint="#00000000" 。 我需要使用它,虽然因为我有一个固定的背景图像。

我发现设置下列属性将禁用android的列表视图caching,并且列表滚动平滑(GC不会经常被调用):

 android:scrollingCache="false" android:animationCache="false" 

另外,如果你想摆脱默认的select颜色使用这个:

 android:listSelector="#00000000" 

你应该使用DDMS和它的Allocation Tracker找出产生这么多对象的东西。 请注意,String.format()因生成大量垃圾而广为人知。

http://developer.android.com/resources/articles/track-mem.html

我实际上是通过你的代码,并砍了一下,使其在我的设备上工作。 我可以确认你的ListAdapter就好了,问题在别处。

这就是你在createDefaultLabelsList()方法中所做的。

 mItemsInList.clear(); ItemInRootList item; do { item = new ItemInRootList(); item.isLabel = true; item.id = c.getString(c.getColumnIndex(Labels.KEY_ID)); item.title = Labels.label(item.id); //TODO: fix this label.label = c.getString(c.getColumnIndex(Labels.KEY_LABEL)); item.unreadCount = c.getString(c.getColumnIndex(Labels.KEY_UNREAD_COUNT)); mItemsInList.add(item); } while (c.moveToNext()); 

看来,不要在你的循环中使用本地variables是你的性能问题的原因,不pipe你信不信。 将其replace为:

 mItemsInList.clear(); do { ItemInRootList item = new ItemInRootList(); item.isLabel = true; item.id = c.getString(c.getColumnIndex(Labels.KEY_ID)); item.title = Labels.label(item.id); //TODO: fix this label.label = c.getString(c.getColumnIndex(Labels.KEY_LABEL)); item.unreadCount = c.getString(c.getColumnIndex(Labels.KEY_UNREAD_COUNT)); mItemsInList.add(item); } while (c.moveToNext()); 

并享受你的快速清单! 这使用ListActivity在htc hero(2.1)和Xoom(3.1)上解决了这个问题。

这就是我本来应该做的 – 但是我不确定这是怎么解释你最初实施的缓慢的行为。

我发现这一点,因为为了重现你的问题,我不得不取代你build立列表的方式,只是创build了一个10K随机标签列表,并看着它滚动非常快。 然后我注意到我们没有使用完全相同的循环,当我添加10k个不同的引用时,您一次又一次地添加相同的引用。 我切换到您的实施,并转载错误。

这个

  String text = String.format("%1$s (%2$s)", item.title, item.unreadCount); 

会多次调用GC。

除了最终生成的string之外, String.format()还会分配一些临时内存。

另一种方法来做到这一点。

使用StringBuilder类,就像这样。

 StringBuilder builder = new StringBuilder(128); @Override public View getView(int position, View convertView, ViewGroup parent) { SuscriptionsViewsHolder holder; ItemInRootList item = mItemsInList.get(position); if (convertView == null) { convertView = mInflater.inflate(R.layout.label, null); holder = new SuscriptionsViewsHolder(); holder.label = (TextView) convertView.findViewById(R.id.label_label); holder.icon = (ImageView) convertView.findViewById(R.id.label_icon); convertView.setTag(holder); } else { holder = (SuscriptionsViewsHolder) convertView.getTag(); } 

String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);

  builder.setLength(0); builder.append(item.title).append(" (").append(item.unreadCount).append(")"); holder.label.setText(builder.toString()); holder.icon.setImageResource(item.isLabel ? R.drawable.folder : R.drawable.file ); return convertView; } 

String text = String.format(“%1 $ s(%2 $ s)”,item.title,item.unreadCount); 可能是String.format()创build了很多污染GC的中间string。 尝试StringBuilder并尝试caching它构build的string。 也作为@ tmho说,尝试holder.icon.setImageDrawable(); 而不是holder.icon.setImageResource(); 因为setImageResource()每次都会创build一个Drawable对象。 每个Drawable约为50-60个字节。 在这种情况下,似乎heave位图不重复,只是可绘制的容器,但它可能足以导致定期的GC调用。

尝试holder.icon.setImageBitmap(); 而不是holder.icon.setImageResource();

这从4.4.4 KitKat更新开始。 重新运行代码已经正常代码,并出现此问题。 由于滚动速度非常慢,ListView现在完全无法使用。 (Nexus 4)

而且我没有使用任何格式化器。

View正在滚动时,出现许多GC消息。

我有类似的问题,我的列表视图将显示来自互联网的图像,并将其保存在SD卡上。 当应用程序启动后,本地图像将被使用,然后当我滚动列表视图,大量的GC出现在logcat上,并且视图将昏迷。

我注意到,当第一次启动应用程序,所有来自互联网的图像,列表视图正常显示。

顺便说一下,我使用了http://code.google.com/p/android-imagedownloader/中的android-imagedownloader,并添加了一些本地文件caching代码&#x3002;

所以这里是我的解决scheme,当它从本地文件加载图像,我将添加到内存caching(静态HashMap)的位图对象,此后,当ListView上下滚动,它会从内存caching中获取图像, GC不会再发生。