我正在使用我的ListView视图持有者模式。当我运行我的应用程序一段时间后,我发现我保留了很多ViewHolder实例:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.foo, null);
holder = new ViewHolder();
holder.username = (TextView) convertView.findViewById(R.id.username);
holder.address = (TextView) convertView.findViewById(R.id.address);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
...
return convertView;
}
private static class ViewHolder {
TextView username;
TextView address;
}
我正在使用UI猴子工具来运行5,000次随机操作。第一次运行,我在内存中看到了大约6个ViewHolder实例。然后是大约10,然后是15,依此类推。我通过内存分析器工具看到了这些。
我不确定为什么没有清理这些额外的实例。每个ViewHolder实例都保留“mTag”,然后在其下面有一棵大树(整个视图)。
有没有人见过这个?
------------------更新-------------------------
仍然没有任何运气找出正在发生的事情。在Memory Analyzer工具中,我看到了我对视图持有者实例不了解的引用(目前大约有21个实例,而我的屏幕在任何给定时间只能容纳大约6个实例)。显示传入的引用后,它们看起来像这样:
com.me.project.MyAdapter$ViewHolder
|-- mTag
| |-- mParent (TextView)
| |-- mParent (TextView)
| |-- [1] java.lang.Object[12]
| |-- array (mCurrentScrap android.widget.AbsListView$RecycleBin)
|
com.me.project.MyAdapter$ViewHolder
|-- mTag (this one looks ok, only lists the two TextView references)
|
com.me.project.MyAdapter$ViewHolder
|-- mTag ..
|-- [1] android.view.View[12]
|-- mChildren android.widget.ListView
|-- this$0 android.widget.ListView$FocusSelector
|-- this$0 android.widget.AdapterView$AdapterDataSetObserver
...
因此,某些实例只与ViewHolder的成员变量建立连接(有意义),其他实例与其他数据的连接似乎是父ListView的一部分,但与ViewHolder本身无关。这些似乎没有得到清理,过了一段时间我会有数十个这样的人。
为了清楚起见,在我最近的mem快照中的20个左右的实例中,我将有多个实例看起来像上面的每个父节点(因此多个实例具有ListView $ FocusSelector在它下面的孩子等。)
由于
答案 0 :(得分:0)
每次为ViewHolder
项创建新View
时都会创建一个新的Adapter
,所以正确的问题是我在适配器启动之前创建了多少个视图重用它们?你可以通过添加日志“为位置创建新视图”+位置和“重新使用位置视图”+位置来检查这一点。
但是,我相信一切都还可以,并且您实际上拥有与您的适配器需要一样多的新视图(尽可能多的列表项可以放在屏幕上)。如果情况并非如此(并且您实际上得到了像100这样的荒谬数字),请检查您是否有许多适配器,例如在每个onResume()
上创建一个新适配器,但仍然保留对旧适配器的引用。哪一个,你很可能没有注意到,所以你拥有的ViewHolders数量可能与你列表的新视图数量正确对应。
答案 1 :(得分:0)
您在if (convertView == null)
声明中缺少以下一行:
convertView.setTag(holder);
要理解的关键概念是ListView使用回收器,因此它只想实例化适合屏幕的视图。当视图从屏幕移开时,回收者将其传递给getView
并要求您重新填充它。然后,它将使用新数据将相同的视图移动到正确的位置(列表的顶部或底部)。