在ListAdapter中使用ImageView时出现OutOfMemoryError?

时间:2015-10-07 01:34:32

标签: java android android-studio picasso

我有一个包含很多行的ListView。每行包含文本和图像。 我扩展了BaseAdapter以创建一个ListAdapter,用于加载Picasso的图像。

她是我的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;
  }

}

以下是完整的课程:https://gist.github.com/confile/a6ed64bde15aca44fd06

当向下滚动列表时,我收到很多内存警告,在几张图片后我的列表崩溃了。这是我得到的错误:

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)

如何有效地回收ImageView以防止内存泄漏?

1 个答案:

答案 0 :(得分:1)

我相信您的问题出在placeholderImage。您应该检查R.drawable.card_image的大小。尝试删除占位符并运行一些测试。

关于优化

好吧,为了有效地回收列表并使用新的材料标准,您应该使用RecyclerView。 RecyclerView的核心是模块化和优化。它非常流畅和可扩展。

但这并不意味着您无法优化列表视图。您可以实施 ViewHolder pattern 。你肯定会得到一些记忆。 代码取自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;
 }

您还应该记住,Android应用程序通常没有太多可用资源。也就是说,您应该始终考虑图像的最终尺寸。始终避免加载大于您需要的图像。 如果您的应用程序将消耗更多内存,那么通常的应用程序可以考虑使用largeHeap在清单中请求更多资源。 Documentation