How to properly use Picasso library in RecycleView

862 views Asked by At

I have a product list made with a Recycle View with CardViews inside. Each CardView have an Image of the product.

The problem is that I'm getting a

"java.lang.OutOfMemoryError"

. I think is because I'm using Picasso in my RecycleView Adapter.

Here is the code:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.List;

import uk.co.something.R;
import uk.co.something.Product;


public class ProductListAdapter extends RecyclerView.Adapter<ProductListAdapter.ViewHolder> {
    private List<Product> products;
    private int rowLayout;
    private Context appContext;

    public ProductListAdapter(List<Product> products, int rowLayout, Context appContext) {
        this.products = products;
        this.rowLayout = rowLayout;
        this.appContext = appContext;
    }

    @Override
    public ProductListAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowLayout, viewGroup, false);
        return new ProductListAdapter.ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ProductListAdapter.ViewHolder viewHolder, int i) {
        Product product = products.get(i);
        Picasso.with(appContext).load(product.getUrl().getImage()).into(viewHolder.image);
        Picasso.with(appContext).load(product.getRetailer().getIcon()).into(viewHolder.logo);
        String productPrice = (product.getPrice().getDiscounted() == null || Double.parseDouble(product.getPrice().getDiscounted()) <= 0) ? product.getPrice().getCost() : product.getPrice().getDiscounted();
        viewHolder.price.setText("\u00a3" + productPrice);

    }

    @Override
    public int getItemCount() {
        return products == null ? 0 : products.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView image;
        public ImageView logo;
        public TextView price;

        ViewHolder(View itemView) {
            super(itemView);
            image = (ImageView) itemView.findViewById(R.id.product_list_cardview_image);
            logo = (ImageView) itemView.findViewById(R.id.product_list_cardview_logo);
            price = (TextView) itemView.findViewById(R.id.product_list_cardview_price);
        }

    }
}

The error I'm getting is this one:

E/AndroidRuntime: FATAL EXCEPTION: main Process: uk.co.lexel.dimble, PID: 5503 java.lang.OutOfMemoryError: Failed to allocate a 11760012 byte allocation with 4269112 free bytes and 4MB until OOM at dalvik.system.VMRuntime.newNonMovableArray(Native Method) at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609) at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444) at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1080) at android.content.res.Resources.loadDrawableForCookie(Resources.java:2635) at android.content.res.Resources.loadDrawable(Resources.java:2540) at android.content.res.Resources.getDrawable(Resources.java:806) at android.content.Context.getDrawable(Context.java:458) at android.support.v4.content.ContextCompatApi21.getDrawable(ContextCompatApi21.java:30) at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:372) at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:202) at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:190) at android.support.v7.content.res.AppCompatResources.getDrawable(AppCompatResources.java:100) at android.support.v7.widget.AppCompatImageHelper.loadFromAttributes(AppCompatImageHelper.java:54) at android.support.v7.widget.AppCompatImageView.(AppCompatImageView.java:66) at android.support.v7.widget.AppCompatImageView.(AppCompatImageView.java:56) at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:106) at android.support.v7.app.AppCompatDelegateImplV9.createView(AppCompatDelegateImplV9.java:1021) at android.support.v7.app.AppCompatDelegateImplV9.onCreateView(AppCompatDelegateImplV9.java:1080) at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:47) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:746) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704) at android.view.LayoutInflater.rInflate(LayoutInflater.java:835) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:798) at android.view.LayoutInflater.rInflate(LayoutInflater.java:838) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:798) at android.view.LayoutInflater.rInflate(LayoutInflater.java:838) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:798) at android.view.LayoutInflater.inflate(LayoutInflater.java:515) at android.view.LayoutInflater.inflate(LayoutInflater.java:423) at uk.co.something.controllers.activities.ProductListAdapter.onCreateViewHolder(ProductListAdapter.java:35) at uk.co.something.controllers.activities.ProductListAdapter.onCreateViewHolder(ProductListAdapter.java:22) at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6290) at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5478) at android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:270) at android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:324) at android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:337) at android.support.v7.widget.GapWorker.prefetch(GapWorker.java:344) at android.support.v7.widget.GapWorker.run(GapWorker.java:370) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

In the logs says that the problem is here:

View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowLayout, viewGroup, false);

Can someone help me?

3

There are 3 answers

0
Siddhesh Dighe On BEST ANSWER

You're getting java.lang.OutOfMemoryError error because your app is exceeding the total memory provided to it.

In your app, this might be because the images you're displaying in your recyclerView are large in size and they are not scaled to the size of the imageview before they are displayed

For scaling your images properly using Picasso you can use the .fit() method in Picasso

eg

Picasso  
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.fit()
// call .centerInside() or .centerCrop() to avoid a stretched image
.into(imageViewFit);

Or Instead of Picasso you can use Glide

  compile 'com.github.bumptech.glide:glide:3.7.0'

eg

Glide.with(context) 
     .load(image)
     .asBitmap()‌​.transform(new CenterCrop(context)) 
     .diskCacheStrategy( DiskCacheStrategy.NONE ) 
     .skipMemoryCache( true ) 
     .int‌​o(your_imageview)

You can also increase the default memory assigned to your app by enabling large heap in your Manifest

eg

android:largeHeap="true"

For more on Scaling Images and loading Images you can check this link out : https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

0
Ajaj Patel On

In my project i have used almost all libraries to load images in recylerview, but all libraries have some problem. But Universal Image Loader is the best when it comes to load images in recyclerview.

0
Patel Vicky On

Prevent java.lang.OutOfMemoryError simple use Glide Image Loader with smooth scrolling.Use below link Glide Tutorial