没有使用setTag(),似乎阻止了gc?

时间:2011-08-09 22:51:50

标签: android

我正在使用我的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在它下面的孩子等。)

由于

2 个答案:

答案 0 :(得分:0)

每次为ViewHolder项创建新View时都会创建一个新的Adapter,所以正确的问题是我在适配器启动之前创建了多少个视图重用它们?你可以通过添加日志“为位置创建新视图”+位置和“重新使用位置视图”+位置来检查这一点。

但是,我相信一切都还可以,并且您实际上拥有与您的适配器需要一样多的新视图(尽可能多的列表项可以放在屏幕上)。如果情况并非如此(并且您实际上得到了像100这样的荒谬数字),请检查您是否有许多适配器,例如在每个onResume()上创建一个新适配器,但仍然保留对旧适配器的引用。哪一个,你很可能没有注意到,所以你拥有的ViewHolders数量可能与你列表的新视图数量正确对应

答案 1 :(得分:0)

您在if (convertView == null)声明中缺少以下一行:

 convertView.setTag(holder);

要理解的关键概念是ListView使用回收器,因此它只想实例化适合屏幕的视图。当视图从屏幕移开时,回收者将其传递给getView并要求您重新填充它。然后,它将使用新数据将相同的视图移动到正确的位置(列表的顶部或底部)。