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

Android 没有使用setTag()对,似乎阻止了gc?,android,Android,我在ListView中使用了视图持有者模式。当我运行我的应用程序一段时间后,我发现我保留了很多ViewHolder实例: @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.lay

我在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 monkey工具运行5000个随机操作的批处理。第一次运行时,我在内存中看到大约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子节点,等等)


谢谢

每次为
适配器
项目创建新的
视图时,您都会创建一个新的
视图保持架
,因此正确的问题是,在适配器开始重用它们之前,我要创建多少视图?您可以简单地通过添加日志“为位置创建新视图”+位置和“为位置重用视图”+位置来检查这一点


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

如果(convertView==null)
语句中缺少以下行:

 convertView.setTag(holder);

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

问题似乎是为适配器中的每个项目创建了一个新视图。因此,如果您的适配器中有大量项,那么尝试将它们全部显示在列表中时会耗尽内存。视图需要循环使用。您所描述的非常有意义,我认为它应该是这样工作的。我继续看到mem dumps中的持镜者不断攀升。我将尝试组合一个更简单的示例项目,也许我引入了一些其他代码,导致了ViewHolder泄漏。@elevine,他可能留下了setTag的假设是正确的,你想得真周到:)我认为应该在他的代码片段的“…”中的某个地方,这就是问题所在,在任何给定的时间,我只能在屏幕上显示大约6个这样的视图,但在我最近的mem转储中,我有26个ViewHolder实例。这似乎真的很高!您是否需要将convertView.setTag(holder)添加到您的代码中,或者只是在您的问题中忽略了它?抱歉,是的,我只是忘记将其添加到问题中。