O listview sai da exceção de memória, mas sem vazamentos de memória?

Depois do Honeycomb, o Google disse que os bitmaps são gerenciados pelo heap (falamos sobreAqui), portanto, se um bitmap não estiver mais acessível, podemos assumir que o GC cuida dele e o libera.

Eu queria criar uma demonstração que mostrasse a eficiência das idéias mostradas para a palestra listView (deAqui), então eu fiz um pequeno aplicativo. O aplicativo permite que o usuário pressione um botão e, em seguida, o listview rola todo o caminho até a parte inferior, enquanto ele tem 10000 itens, cujo conteúdo é os itens android.R.drawable (nome e imagem).

Por alguma razão, eu fico sem memória mesmo que não salve nenhuma das imagens, então minha pergunta é: como poderia ser? O que é que eu estou sentindo falta?

Eu testei o aplicativo em um Galaxy S III, e ainda continuo recebendo exceções de memória se eu usar a versão nativa do adaptador. Não entendo porque ocorre, já que não armazeno nada.

Aqui está o código:

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: Eu sei que existem otimizações para este código (usando o convertView e o padrão de design viewHolder) como eu mencionei o vídeo da listView feita pelo Google. Acredite, eu sei o que é melhor; Este é o ponto principal do código.

O código acima deve mostrar que é melhor usar o que você (e o vídeo) mostra. Mas primeiro preciso mostrar o caminho ingênuo; até mesmo a maneira ingênua ainda deve funcionar, já que eu não armazeno os bitmaps ou as visualizações, e já que o Google fez o mesmo teste (daí ter um gráfico de comparação de desempenho).

questionAnswers(4)

yourAnswerToTheQuestion