1

I am building a music app with images in it and I am using Picasso to load the images. The target I'm using with Picasso is the following:

Target target = new Target() {

    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        try {

            if (artNormal != null) {
                artNormal.recycle();
                artNormal = null;
            }

            artNormal = Bitmap.createBitmap(bitmap);
            TransitionDrawable td = null;


            if (upNextShowing) {

                Bitmap scaled = Bitmap.createScaledBitmap(artNormal, 10, 10, true);
                td = new TransitionDrawable(new Drawable[]{
                        playerArt.getDrawable(),
                        new BitmapDrawable(context.getResources(), scaled)
                });

                // Updated Part -- Updated Part
                if(scaled!=null){
                scaled.recycle();
                scaled = null;
            }

            } else {

                td = new TransitionDrawable(new Drawable[]{
                        playerArt.getDrawable(),
                        new BitmapDrawable(context.getResources(), bitmap)
                });

            }
            td.setCrossFadeEnabled(true);
            playerArt.setImageDrawable(td);
            td.startTransition(1000);

            new GetBlurredAlbumArt(artNormal).execute();

        } catch (Exception e) {

            e.printStackTrace();
            Log.e("LargePlayer", "Error :" + e.getLocalizedMessage());
            Log.e("LargePlayer", "Error :" + e.getCause());
        }

    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
        Log.e("LargePlayer", "Picasso Load Failed");
        TransitionDrawable td = null;
        if (artNormal != null) {
            artNormal.recycle();
            artNormal = null;
        }
        artNormal = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.album_art_large);
        artNormal = Bitmap.createScaledBitmap(artNormal, 800, 800, true);

        if (upNextShowing) {

            Bitmap scaled = Bitmap.createScaledBitmap(artNormal, 10, 10, true);
            td = new TransitionDrawable(new Drawable[]{
                    playerArt.getDrawable(),
                    new BitmapDrawable(context.getResources(), scaled)
            });

                // Updated Part -- Updated Part
                if(scaled!=null){
                scaled.recycle();
                scaled = null;
            }

        } else {

            td = new TransitionDrawable(new Drawable[]{
                    playerArt.getDrawable(),
                    new BitmapDrawable(context.getResources(), BitmapFactory.decodeResource(context.getResources(),
                            R.drawable.album_art_large))
            });

        }
        td.setCrossFadeEnabled(true);
        playerArt.setImageDrawable(td);
        td.startTransition(1000);
        new GetBlurredAlbumArt(artNormal).execute();

    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {

    }

};
class GetBlurredAlbumArt extends AsyncTask<Void, Void, Bitmap> {

    Bitmap bitmap;

    public GetBlurredAlbumArt(Bitmap bitmap) {
        this.bitmap = bitmap;

    }

    @Override
    protected Bitmap doInBackground(Void... params) {

        bitmap = Bitmap.createScaledBitmap(bitmap, 300, 300, true);

        bitmap = ImageUtilties.fastblur(bitmap, 100);

        try {
            return bitmap;
        } catch (Exception e) {
            Log.e("LargePlayer", "Error :" + e.getLocalizedMessage());
            Log.e("LargePlayer", "Error :" + e.getCause());
        }
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);

        TransitionDrawable td = new TransitionDrawable(new Drawable[]{
                blurredColors.getDrawable() != null ? blurredColors.getDrawable() : new ColorDrawable(Color.WHITE),
                new BitmapDrawable(context.getResources(), bitmap)
        });

        td.setCrossFadeEnabled(true);
        blurredColors.setImageDrawable(td);

        td.startTransition(1000);


    }
}

Every time the image is changed memory consumption is increased by 2-3 MB. Memory usage could increase upto 200 MB (which is surely not acceptable). There are only 2 imageviews that change in the activity. Image sizes are nearly upto 1200x1200. I know they are pretty big but I am recycling the bitmap every time before setting but still the Memory usage increases like anything. After that application crashes. It also sometimes gives the error trying to use a recycled Bitmap. Also tried

android:largeHeap="true"

But I need to decrease memory usage. Please help!

Akshay Sharma
  • 418
  • 3
  • 12
  • 1
    Take a look here [link](http://stackoverflow.com/questions/477572/strange-out-of-memory-issue-while-loading-an-image-to-a-bitmap-object) – BOUTERBIAT Oualid May 30 '16 at 08:40
  • I guess the link is about scaling of bitmaps when loading. But Picasso resizes the image itself using resize(). Are you saying picasso resize the image after being loaded into memory? – Akshay Sharma May 30 '16 at 08:57

2 Answers2

2

One solution is to try to display the image according the resolution of the screen. For that, as soon as the app starts, store the width and height pixels in your SharedPreferences and then use it to load a scaled down version of image. The other solution that I would advise is, if possible try using Fresco. It uses ashmem cache which can store large amounts of data. I have personally faced this issue with Picasso and couldn't find an elegant solution but after switching to Fresco, all those OOM errors are gone.

Amit Tiwari
  • 3,684
  • 6
  • 33
  • 75
1

The first bitmap is not GC'ed when you decode the second one, because you make many copy of bitmap you need free memory used by bitmap ASAP with recycle.

https://developer.android.com/training/displaying-bitmaps/manage-memory.html

mdtuyen
  • 4,470
  • 5
  • 28
  • 50
  • I didnt get you. Whenever picasso laod a image succesfully it goes to the function onBitmapLoaded() where i am recycling the bitmap if not null. – Akshay Sharma May 30 '16 at 08:55
  • i check that you copy or decode file or scale to make a new bitmap and not recycle them when you finish. – mdtuyen May 30 '16 at 08:58
  • Does the bitmap stays in memory even if the scope is finsihed? – Akshay Sharma May 30 '16 at 09:03
  • yes, it usually is reason of memory leak while use with bitmap – mdtuyen May 30 '16 at 09:06
  • I recycled the scaled bitmap updated in the code but still not much effect. The bitmap image in the AysncTask, whenever i recycle it in post execute, it gives a error that "trying to draw on recycled canvas". – Akshay Sharma May 30 '16 at 09:19
  • you sould recycle it when you not use them not when you init them. pls check isRecycled before call recycled(). – mdtuyen May 30 '16 at 09:48