15

I am using Picasso library latest version 2.4.0 in my app for downloading and caching images. There are roughly 25-30 images of size 300KB-400KB each. I think that this is no way something big or heavy.

Even though the app is running fine, I'm getting out of memory allocations in my logcat. Can anyone explain why is this happening?

Code for loading images in GridView adapter:

Picasso.with(mContext).load(getUrl()).placeholder(R.drawable.placeholder)
            .into(viewholder.image);

Here is my Logcat output:

I/dalvikvm-heap(11142): Grow heap (frag case) to 53.860MB for 2720016-byte allocation
I/dalvikvm-heap(11142): Forcing collection of SoftReferences for 3265936-byte allocation
E/dalvikvm-heap(11142): Out of memory on a 3265936-byte allocation.
I/dalvikvm(11142): "Picasso-/images/posters/34.71.jpg" prio=5 tid=18 RUNNABLE
I/dalvikvm(11142):   | group="main" sCount=0 dsCount=0 obj=0x4283f248 self=0x60a47830
I/dalvikvm(11142):   | sysTid=11196 nice=10 sched=0/0 cgrp=apps/bg_non_interactive I/dalvikvm(11142):   | state=R schedstat=( 2070202497 1858185620 3947 ) utm=172 stm=35 core=3
I/dalvikvm(11142):   at android.graphics.Bitmap.nativeCreate(Native Method)
I/dalvikvm(11142):   at android.graphics.Bitmap.createBitmap(Bitmap.java:726)
I/dalvikvm(11142):   at android.graphics.Bitmap.createBitmap(Bitmap.java:703)
I/dalvikvm(11142):   at android.graphics.Bitmap.createBitmap(Bitmap.java:636)
I/dalvikvm(11142):   at com.squareup.picasso.BitmapHunter.transformResult(BitmapHunter.I/dalvikvm(11142):   at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:168)
I/dalvikvm(11142):   at com.squareup.picasso.BitmapHunter.run(BitmapHunter.java:111)
I/dalvikvm(11142):   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390I/dalvikvm(11142):   at java.util.concurrent.FutureTask.run(FutureTask.java:234)
I/dalvikvm(11142):   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.I/dalvikvm(11142):   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.I/dalvikvm(11142):   at java.lang.Thread.run(Thread.java:841)
I/dalvikvm(11142):   at com.squareup.picasso.Utils$PicassoThread.run(Utils.java:408)
I/dalvikvm-heap(11142): Forcing collection of SoftReferences for 3265936-byte allocation
E/dalvikvm-heap(11142): Out of memory on a 3265936-byte allocation.
I/dalvikvm(11142): "Picasso-/images/posters/34.71.jpg" prio=5 tid=17 RUNNABLE
I/dalvikvm(11142):   | group="main" sCount=0 dsCount=0 obj=0x42841b88 self=0x5ec91f90
I/dalvikvm(11142):   | sysTid=11183 nice=10 sched=0/0 cgrp=apps/bg_non_interactive I/dalvikvm(11142):   | state=R schedstat=( 2050467088 1713164574 3713 ) utm=172 stm=32 core=3
I/dalvikvm(11142):   at android.graphics.Bitmap.nativeCreate(Native Method)
I/dalvikvm(11142):   at android.graphics.Bitmap.createBitmap(Bitmap.java:726)
I/dalvikvm(11142):   at android.graphics.Bitmap.createBitmap(Bitmap.java:703)
I/dalvikvm(11142):   at android.graphics.Bitmap.createBitmap(Bitmap.java:636)
I/dalvikvm(11142):   at com.squareup.picasso.BitmapHunter.transformResult(BitmapHunter.I/dalvikvm(11142):   at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:168)
I/dalvikvm(11142):   at com.squareup.picasso.BitmapHunter.run(BitmapHunter.java:111)
I/dalvikvm(11142):   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390I/dalvikvm(11142):   at java.util.concurrent.FutureTask.run(FutureTask.java:234)
I/dalvikvm(11142):   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.I/dalvikvm(11142):   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.I/dalvikvm(11142):   at java.lang.Thread.run(Thread.java:841)
I/dalvikvm(11142):   at com.squareup.picasso.Utils$PicassoThread.run(Utils.java:408)
Ed Holloway-George
  • 5,092
  • 2
  • 37
  • 66
Adeel Ahmad
  • 939
  • 1
  • 10
  • 20
  • 2
    "I think that this is no way something big or heavy" -- those images are *huge*. Image files like PNG and JPEG are compressed; your heap space for those images are going to be several MB apiece. – CommonsWare Nov 14 '14 at 20:32
  • @CommonsWare I said that considering camera images are 20-30 times bigger than those. Thanks for pointing me in the right direction. Should I worry or ignore this issue as App is running fine. – Adeel Ahmad Nov 14 '14 at 20:35
  • 2
    "I said that considering camera images are 20-30 times bigger than those" -- you cannot load 25-30 full-resolution camera images into memory at one time either. "App is running fine" -- no, your app is crashing, as is evidenced by your stack trace. You need to have Picasso downsample the image to a size that is relevant for your `GridView`, such as by chaining a call to `fit()` in your code shown above. – CommonsWare Nov 14 '14 at 20:39
  • @CommonsWare WowW. Just adding .fit() has fixed the issue, no more out of memory in logcat. Plus the RAM usage has gone from 70MB to 40MB. Glad I asked this. – Adeel Ahmad Nov 14 '14 at 20:49
  • Maybe the next answer can be help you http://stackoverflow.com/questions/24916867/picasso-gives-out-of-memory-when-load-an-image – yaircarreno Jan 19 '16 at 02:31

4 Answers4

27

Your original code was downloading the full image, loading the full image into memory, then having Android scale the image down to fit your ImageView.

In this case, you do not need the full image in memory -- you need something closer in size to what the ImageView is.

fit() on Picasso handles this. It uses inSampleSize on BitmapFactory.Options to downsample the image as it is being loaded into memory, to get you something around the size of the ImageView, letting Android scale from there. This will significantly reduce the memory footprint of each image, particularly depending on how big of an ImageView you are using.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • .fit() was not perfect. Simply calling .fit() was better than before, but was still causing memory exceptions on fast scrolling. Calling resize() with dimensions completely fixed the issue. – Adeel Ahmad Nov 14 '14 at 21:40
  • 1
    @AdeelAhmad: Hmmm... for some reason, I thought `fit()` did a `resize()`, but I'm probably mis-remembering. Glad that it's working for you! – CommonsWare Nov 14 '14 at 21:47
  • 3
    I just checked the source, fit() does call resize()--immediately if target has a size or after target has has a size. – enl8enmentnow May 25 '15 at 08:00
  • @CommonsWare, it does call if `fit()` is called. Check out the function `RequestCreator.into(target, callback)` – Jimit Patel Sep 29 '16 at 06:14
1

Watch out with .fit()!

Its better to server the right image sizes from the server, by sending device screen width to the server, and serving the right image for the device. In this way tou dont need todo scaling.

TjerkW
  • 2,086
  • 21
  • 26
  • I guess server should have a set of pre-scaled images to avoid too much additional computation, but sounds like a good idea. – Simon Ninon Jun 23 '17 at 16:48
1

All the others answers sounds good when mentioning .fit().

I think it can be interesting to mention downside of .fit though:

fit() is measuring the dimensions of the target ImageView and internally uses resize() to reduce the image size to the dimensions of the ImageView. There are two things to know about fit(). First, calling fit() can delay the image request since Picasso will need to wait until the size of the ImageView can be measured. Second, you only can use fit() with an ImageView as the target (we'll look at other targets later).

The advantage is that the image is at the lowest possible resolution, without affecting its quality. A lower resolution means less data to be hold in the cache. This can significantly reduce the impact of images in the memory footprint of your app. In summary, if you prefer a lower memory impact over a little faster loading times, fit() is a great tool.

TLDR:

  • Can only be used with ImageView targets
  • May slow down loading

Source: https://futurestud.io/tutorials/picasso-image-resizing-scaling-and-fit

Community
  • 1
  • 1
Simon Ninon
  • 2,371
  • 26
  • 43
0

Should like this

Picasso.with(mContext).load(getUrl()).fit().placeholder(R.drawable.placeholder)
        .into(viewholder.image);
Son Nguyen Thanh
  • 1,199
  • 15
  • 19