2
votes

I am facing the outOfMemory exception while loading images with Picasso. I am creating picasso builder with OkHttp and created Picasso Singleton class to cache images.

Scenario: I have 100+ image feeds which are to be loaded. I am getting images in sets and each set has 25 image url which i am setting with Picasso. I am using recyclerview and whenever new set of images come in i am calling adapter.notifyDataSetChanged(). I am loading first set - no issues, for 2nd and 3rd set when making retrofit to get next set and adding to existing list and calling - adapter.notifyDataSetChanged(). For 3rd set when I call adapter.NotifyDataSetChanged() app crashes with outOfMemoryException

BUT

When i load all 3 sets of 75 images, I don't face any issues.

Code: Application Class - where I am building Picasso.

 Picasso.Builder builder = new Picasso.Builder(this)
            .memoryCache(new LruCache(24000));
    builder.downloader(new OkHttpDownloader(this,Integer.MAX_VALUE));
    Picasso built = builder.build();
    built.setLoggingEnabled(true);

Picasso Singleton Class:

public class PicassoCache {

/**
 * Static Picasso Instance
 */
private static Picasso picassoInstance = null;

/**
 * PicassoCache Constructor
 *
 * @param context application Context
 */
private PicassoCache (Context context) {

    Downloader downloader   = new OkHttpDownloader(context, Integer.MAX_VALUE);
    Picasso.Builder builder = new Picasso.Builder(context);
    builder.downloader(downloader);

    picassoInstance = builder.build();
}

/**
 * Get Singleton Picasso Instance
 *
 * @param context application Context
 * @return Picasso instance
 */
public static Picasso getPicassoInstance (Context context) {

    if (picassoInstance == null) {

        new PicassoCache(context);
        return picassoInstance;
    }

    return picassoInstance;
}

}

Code where I am setting/loading images with Picasso.

  PicassoCache.getPicassoInstance(context).load(url).placeholder(R.mipmap.banner_placeholder).into(mView);

Code where I am updating the existing list to load when there is data set changed.

Inside Retorfit OnSuccess message:

    if (response.code() == 200) {

List<CampaignCard> newCampaigns = response.body().getCampaigns();


for (int i = 0; i < newCampaigns.size(); i++) {
    if (!campaignCards.contains(newCampaigns.get(i))) {
        campaignCards.add(newCampaigns.get(i));
    }
}

dashBoardAdapter.notifyDataSetChanged();

} else if (response.code() == Params.CODE_422) {
    Utils.ShowServiceErrorMessages(getActivity(), response);
} else if (response.code() == Params.CODE_401) {
    Utils.Logout(getActivity());
}

Adapter Class:

public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardViewHolder> implements View.OnClickListener {


    private static final int VIEW_TYPE_CAMPAIGN = 1;
    private static final int VIEW_TYPE_FEED = 2;

    DashboardViewHolder holder;


    protected List list;
    protected int viewTypeLayout;
    Context context;
    int position;

    Dashboard.DashboardActionList actionList;
    Map<String, SourceContent> mPreviewLinkMapper;
    ViewGroup parent;

    //Picasso p;
    public DashboardAdapter(List list, int viewTypeLayout) {
        this.list = list;
        this.viewTypeLayout = viewTypeLayout;
    }

    public DashboardAdapter(List list, int viewTypeLayout, Context context, Map<String, SourceContent> mPreviewLinkMapper) {
        this.list = list;
        this.viewTypeLayout = viewTypeLayout;
        this.mPreviewLinkMapper = mPreviewLinkMapper;
    }

    @Override
    public DashboardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        context = parent.getContext();
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed, parent, false);
        return new DashboardViewHolder(view, 2);

    }

    @Override
    public void onBindViewHolder(DashboardViewHolder holder, final int position) {
        BindFeedData(holder, position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemCount() {
        return list.size();
    }





    @SuppressWarnings("unchecked")
    private void BindFeedData(DashboardViewHolder holder, int position) {
        List<Feed> feeds = (List<Feed>) list;
        if (holder.mBannerImage != null) {
            if (feeds.get(position).getCampaign().getParticipation().getType().toLowerCase().contains("video")) {

                holder.mPlayIcon.setVisibility(View.VISIBLE);
                String url = feeds.get(position).getCampaign().getParticipation().getThumbnail_url();
                Utils.LoadImages(context, url, holder.mBannerImage, false);

            } else if (feeds.get(position).getCampaign().getParticipation().getType().toLowerCase().contains("gif")) {

                holder.mPlayIcon.setVisibility(View.GONE);
                String url = feeds.get(position).getCampaign().getParticipation().getPost_file();
                Utils.loadGif(context, url, holder.mBannerImage);

            } else if (feeds.get(position).getCampaign().getParticipation().getType().toLowerCase().contains("link") ||
                    feeds.get(position).getCampaign().getParticipation().getType().toLowerCase().contains("youtube")) {

                holder.mPlayIcon.setVisibility(View.GONE);
                String slug = feeds.get(position).getCampaign().getSlug();
                List<String> images = mPreviewLinkMapper.get(slug).getImages();
                Utils.LoadImages(context, images.get(0), holder.mBannerImage, false);

            } else {
                holder.mPlayIcon.setVisibility(View.GONE);
                holder.mBannerImage.setVisibility(View.VISIBLE);
                String url = feeds.get(position).getCampaign().getParticipation().getPost_file();
                Utils.LoadImages(context, url, holder.mBannerImage, false);
            }
        }

        if (holder.mBrandLogo != null) {
            Utils.LoadImages(context, feeds.get(position).getInfluencer().getProfile_picture_url(), holder.mBrandLogo, true);
        }


        holder.mTitle.setText(feeds.get(position).getInfluencer().getName());
        holder.mSubTitle.setText(feeds.get(position).getCampaign().getName());
        holder.mTime.setText(feeds.get(position).getCampaign().getTimestamp());
        holder.mDescription.setText(feeds.get(position).getCampaign().getParticipation().getPost_content());
        holder.mEngagement.setText(feeds.get(position).getCampaign().getParticipation().getMetrics().getEngagements());
        holder.mImpresion.setText(feeds.get(position).getCampaign().getParticipation().getMetrics().getImpressions());
    }



    }


    public static class DashboardViewHolder extends RecyclerView.ViewHolder {

        ImageView mBannerImage, mFacebook, mTwitter, mInstagram, mPlayIcon, mHotIcon, mLocationIcon;
        CircularImageView mBrandLogo;
        CustomTextViewRegular mDescription, mTime, mOption1, mOption2, mOption3;
        CustomTextViewDemi mTitle, mSubTitle, mImpresion, mEngagement;
        LinearLayout mDetailsLayout;

        LinearLayout mOptionLayout1, mOptionLayout2, mOptionLayout3;
        public ViewGroup dropPreview;

        TableRow mTableOptions;

        public DashboardViewHolder(View v, int viewtype) {
            super(v);
            InitFeedViews(v);
          }




        private void InitFeedViews(View v) {
            mTitle = (CustomTextViewDemi) v.findViewById(R.id.adapterHeaderLayoutTitle);
            mSubTitle = (CustomTextViewDemi) v.findViewById(R.id.adapterHeaderLayoutSubTitle);
            mBrandLogo = (CircularImageView) v.findViewById(R.id.adapterHeaderLayoutLogo);
            mTime = (CustomTextViewRegular) v.findViewById(R.id.adapterHeaderLayoutTime);

            mBannerImage = (ImageView) v.findViewById(R.id.adapterFeedBannerImage);
            mPlayIcon = (ImageView) v.findViewById(R.id.adapterFeedPlayIocn);

            mImpresion = (CustomTextViewDemi) v.findViewById(R.id.adapterFeedImpressions);
            mEngagement = (CustomTextViewDemi) v.findViewById(R.id.adapterFeedEngagements);
            mDescription = (CustomTextViewRegular) v.findViewById(R.id.adapterFeedDescription);

            dropPreview = (LinearLayout) v.findViewById(R.id.drop_preview);
        }

    }

}
2
Use recycleview and resize the images while loading.. - sunil sunny
@sunilsunny i am using recyclerview and resizing did not worked. - MobDev
share your adapter code. Mostly bitmaps will produce OOM. Problem may be there. - Ajith Pandian
Try this dashBoardAdapter.notifyItemInserted(list.size() -1); .By calling notifyDataSetChanged you are refreshing the entire view . - sunil sunny
@sunilsunny i tried doing it, now app suddenly closes off when scrolled to 15th - 20th item - but no crash or ANR dialog seen and not error in log - MobDev

2 Answers

1
votes

You would face "out of memory" issues lot in case of picasso library I would suggest you to use Glide library. I had lot "out of memory" exception tried all but at last when i used glide it worked well.

0
votes

Try to resize the image with Picasso library. OutOfMemoryException will be resolved.

For better understanding, refer below link: https://futurestud.io/tutorials/picasso-image-resizing-scaling-and-fit