Reputation: 33297
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
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