列表视图失控内存异常,但没有内存泄漏?(listview gets out of memory ex

2019-06-26 03:00发布

蜂窝后,谷歌表示,位图是由堆(谈到管理在这里 ),所以如果一个位图不再是可访问的,我们可以假设,GC照顾它并释放它。

我想创造一个演示,显示的为ListView讲座(从显示的想法效率在这里 ),所以我做了一个小的应用程序。 该应用程序允许用户按一个按钮,然后在列表视图滚动一路底部,而它有10000项,其中的内容是android.R.drawable项目(姓名和图像)。

出于某种原因,我得到了内存,即使我不保存任何图像,所以我的问题是:这怎么可能? 它是什么,我失踪?

我测试过上的Galaxy S III的应用程序,但我不断收到内存不足异常,如果我使用适配器的原生版本。 我不明白为什么这样的情况,因为我不存储任何。

下面的代码:

public class MainActivity extends Activity
  {
  private static final int LISTVIEW_ITEMS =10000;
  long                     _startTime;
  boolean                  _isMeasuring   =false;

  @Override
  public void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ListView listView=(ListView)findViewById(R.id.listView);
    final Field[] fields=android.R.drawable.class.getFields();
    final LayoutInflater inflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // listen to scroll events , so that we publish the time only when scrolled to the bottom:
    listView.setOnScrollListener(new OnScrollListener()
      {
        @Override
        public void onScrollStateChanged(final AbsListView view,final int scrollState)
          {
          if(!_isMeasuring||view.getLastVisiblePosition()!=view.getCount()-1||scrollState!=OnScrollListener.SCROLL_STATE_IDLE)
            return;
          final long stopTime=System.currentTimeMillis();
          final long scrollingTime=stopTime-_startTime;
          Toast.makeText(MainActivity.this,"time taken to scroll to bottom:"+scrollingTime,Toast.LENGTH_SHORT).show();
          _isMeasuring=false;
          }

        @Override
        public void onScroll(final AbsListView view,final int firstVisibleItem,final int visibleItemCount,final int totalItemCount)
          {}
      });
    // button click handling (start measuring) :
    findViewById(R.id.button).setOnClickListener(new OnClickListener()
      {
        @Override
        public void onClick(final View v)
          {
          if(_isMeasuring)
            return;
          final int itemsCount=listView.getAdapter().getCount();
          listView.smoothScrollToPositionFromTop(itemsCount-1,0,1000);
          _startTime=System.currentTimeMillis();
          _isMeasuring=true;
          }
      });
    // creating the adapter of the listView
    listView.setAdapter(new BaseAdapter()
      {
        @Override
        public View getView(final int position,final View convertView,final ViewGroup parent)
          {
          final Field field=fields[position%fields.length];
          // final View inflatedView=convertView!=null ? convertView : inflater.inflate(R.layout.list_item,null);
          final View inflatedView=inflater.inflate(R.layout.list_item,null);
          final ImageView imageView=(ImageView)inflatedView.findViewById(R.id.imageView);
          final TextView textView=(TextView)inflatedView.findViewById(R.id.textView);
          textView.setText(field.getName());
          try
            {
            final int imageResId=field.getInt(null);
            imageView.setImageResource(imageResId);
            }
          catch(final Exception e)
            {}
          return inflatedView;
          }

        @Override
        public long getItemId(final int position)
          {
          return 0;
          }

        @Override
        public Object getItem(final int position)
          {
          return null;
          }

        @Override
        public int getCount()
          {
          return LISTVIEW_ITEMS;
          }
      });
    }
  }

@all:我知道有优化的代码(使用convertView和viewHolder设计模式),因为我已经提到的由谷歌提出的ListView中的视频。 相信我,我知道什么是更好的; 这是代码的整点。

上面的代码是为了显示它是更好地使用你(和视频)显示。 但首先我需要显示用简单的方式; 甚至用简单的方式应该仍然工作,因为我不存储位图或意见,自谷歌已经做了同样的测试(因此他们得到了性能比较的图表)。

Answer 1:

在蒂姆评论是当场上。 你的代码不使用的事实convertView在其BaseAdapter.getView()方法,并保持每一次膨胀的新观点是,为什么它最终将耗尽内存的主要原因。

我最后一次检查, ListView将继续被不断被返回的所有视图getView()在其内部的“回收站”容器的方法,如果只会被清除ListView从其窗口分离。 这种“回收站”是如何能生产所有这些convertView并提供回getView()在适当的时候。

作为一个测试,你甚至可以注释掉在那里你将图像分配到您的视图代码部分:

                // final int imageResId = field.getInt(null);
                // imageView.setImageResource(imageResId);

而你仍然会得到的内存分配失败在一个点上:)



Answer 2:

这里有两点你的代码:

  1. 正如前面提到的答案,你想创建许多新对象,好,这是主要的原因OutOfMemory的问题。

  2. 您的代码还不够高效,不断加载的所有对象(如刷卡向上/向下滚动),那么,它的滞后。

这里的提示来解决这两个常见问题:

Field field = fields[position % fields.length];
View v = convertView;
ViewHolder holder = null;

if (v == null) {
    v = inflater.inflate(R.layout.list_item,null);
    holder = new ViewHolder();
    holder.Image = (ImageView) inflatedView.findViewById(R.id.imageView);
    holder.Text = (TextView)inflatedView.findViewById(R.id.textView);
    v.setTag(holder);
} else {
    holder = (ViewHolder) v.getTag();
}
return v;

这是一个简单的ViewHolder高效ListView

static class ViewHolder {   
    ImageView Image;
    TextView  Text;
}

非常简单,但非常有效编码。



Answer 3:

您捕捉例外五,但OutOfMemoryError异常是错误,也不例外。 所以,如果你想赶上内存不足你可以写类似

catch(Throwable e){}

要么

catch(OutOfMemoryError e){}


Answer 4:

我已经越来越OOM错误了很久,当我吹我的ListView太多的图像(即使图像已经被压缩)

在你的清单使用,这可能解决您的问题:

android:largeHeap="true"

这会给你的应用程序一个大的内存去努力。

只有当有您想要的输出没有其他方法使用此!

了解使用largeHeap检查这个的缺点答案



文章来源: listview gets out of memory exception, but with no memory leaks?