Michael
Michael

Reputation: 33297

OutOfMemoryError when using ImageView in ListAdapter?

I have a ListView containing a lot rows. Each row contains text and and image. I extended a BaseAdapter to create a ListAdapter which loads an image with Picasso.

Her is my ListAdapter:

public class ServiceCardDtoListAdapter extends BaseAdapter {

...

@Override
  public View getView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(position, convertView, parent, mResource);
  }

  private View createViewFromResource(int position, View convertView, ViewGroup parent,
                                      int resource) {

    RoundedImageView imageView = null;
    TextView title = null;
    TextView location = null;

    if (convertView == null) {
      convertView = inflater.inflate(resource, parent, false);
    }

    try {
      imageView = (RoundedImageView)convertView.findViewById(R.id.service_card_imageview);
      title = (TextView)convertView.findViewById(R.id.service_card_title);
      location = (TextView)convertView.findViewById(R.id.service_card_location);
    }
    catch (Exception e) {
      Log.e("ServiceCardDtoListAdapter", "Cannot create view resource IDs are missing", e);
    }

    // get the correct service_card
    ServiceCardDto item = items.get(position);

    // fill the view
    Picasso.with(context)
        .load(item.getImageUrls().get(0))
        .placeholder(R.drawable.card_image)
        .error(R.drawable.card_image)
        .centerCrop()
        .fit()
        .into(imageView);


    title.setText(item.getTitle());
    location.setText(item.getCompanyName());

    return convertView;
  }

}

Here is the full class: https://gist.github.com/confile/a6ed64bde15aca44fd06

When scrolling down the list I get a lot of memory warnings and after a couple of images my list crashes. Here is the error I get:

10-07 02:41:07.724 4482-4482/? I/art: Clamp target GC heap from 67MB to 64MB
10-07 02:41:07.724 4482-4482/? I/art: Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 1% free, 63MB/64MB, paused 546us total 11.300ms
10-07 02:41:07.724 4482-4482/? E/art: Throwing OutOfMemoryError "Failed to allocate a 15321612 byte allocation with 678832 free bytes and 662KB until OOM"
10-07 02:41:07.724 4482-4482/? D/skia: --- allocation failed for scaled bitmap
10-07 02:41:07.724 4482-4482/? D/AndroidRuntime: Shutting down VM
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime: FATAL EXCEPTION: main
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime: Process: com.lorentzos.swipecards.example, PID: 4482
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime: java.lang.OutOfMemoryError: Failed to allocate a 15321612 byte allocation with 678832 free bytes and 662KB until OOM
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:988)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.content.res.Resources.loadDrawableForCookie(Resources.java:2474)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.content.res.Resources.loadDrawable(Resources.java:2381)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.content.res.Resources.getDrawable(Resources.java:787)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.content.res.Resources.getDrawable(Resources.java:752)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.squareup.picasso.RequestCreator.getPlaceholderDrawable(RequestCreator.java:676)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.squareup.picasso.RequestCreator.into(RequestCreator.java:637)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.squareup.picasso.RequestCreator.into(RequestCreator.java:601)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.lorentzos.swipecards.ServiceCardDtoListAdapter.createViewFromResource(ServiceCardDtoListAdapter.java:84)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.lorentzos.swipecards.ServiceCardDtoListAdapter.getView(ServiceCardDtoListAdapter.java:57)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.lorentzos.flingswipe.SwipeFlingAdapterView.layoutChildren(SwipeFlingAdapterView.java:158)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.lorentzos.flingswipe.SwipeFlingAdapterView.refresh(SwipeFlingAdapterView.java:149)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.lorentzos.swipecards.MyActivity$1.onTabSelected(MyActivity.java:78)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.support.design.widget.TabLayout.selectTab(TabLayout.java:837)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.support.design.widget.TabLayout.selectTab(TabLayout.java:809)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.support.design.widget.TabLayout$Tab.select(TabLayout.java:1077)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.support.design.widget.TabLayout$1.onClick(TabLayout.java:643)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.view.View.performClick(View.java:4780)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.view.View$PerformClick.run(View.java:19866)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.os.Handler.handleCallback(Handler.java:739)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:95)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:135)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5257)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:372)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
10-07 02:41:07.725 4482-4482/? E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

How can I recycle the ImageView efficiently to prevent memory leaks?

Upvotes: 0

Views: 191

Answers (1)

Tin Megali
Tin Megali

Reputation: 801

I believe that your problem is in your placeholderImage. You should check the size of R.drawable.card_image. Try to drop the placeholder and run some tests.

Concerning the optimization

Well, to recycle lists efficiently and with the new material standard, you should use the RecyclerView. The RecyclerView was implemented with modularity and optimization in its core. Its very smooth and expansible.

But that doesn't mean you can't optimize your listview. You could implement the ViewHolder pattern. You will gain some memory with it, for sure. Code taken from Android ViewHolder Pattern Example

 // ViewHolder.
 static class ViewHolderItem {
    TextView textViewItem;
 }

@Override
public View getView(int position, View convertView, ViewGroup parent) {

  ViewHolderItem viewHolder;


  // The convertView argument is essentially a "ScrapView" as described is Lucas post 
  // http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
  // It will have a non-null value when ListView is asking you recycle the row layout. 
  // So, when convertView is not null, you should simply update its contents instead of inflating a new row    layout.

  if(convertView==null){

    // inflate the layout
    LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
    convertView = inflater.inflate(layoutResourceId, parent, false);

    // well set up the ViewHolder
    viewHolder = new ViewHolderItem();
    viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.textViewItem);

    // store the holder with the view.
    convertView.setTag(viewHolder);

  }else{
    // we've just avoided calling findViewById() on resource everytime
    // just use the viewHolder
    viewHolder = (ViewHolderItem) convertView.getTag();
  }

  // object item based on the position
  ObjectItem objectItem = data[position];

  // assign values if the object is not null
  if(objectItem != null) {
    // get the TextView from the ViewHolder and then set the text (item name) and tag (item ID) values
    viewHolder.textViewItem.setText(objectItem.itemName);
    viewHolder.textViewItem.setTag(objectItem.itemId);
  }
  return convertView;
 }

You should also keep in mind that Android applications in general doesn't have much resources available. That said, you should always consider the final size of your images. Always avoid to load images bigger then you need. If your application will consume more memory then the usual application you could consider ask for more resources in the manifest with largeHeap. Documentation

Upvotes: 1

Related Questions