Picasso库 - 内存不足

15

我在我的应用中使用最新版本2.4.0的Picasso库来下载和缓存图像,大约有25-30张大小为300KB-400KB的图片。我认为这不是什么大或重的东西。

尽管应用程序运行良好,但我在logcat中遇到了内存分配问题。有人能解释一下为什么会发生这种情况吗?

GridView适配器中加载图像的代码:

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

这是我的Logcat输出:

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)

2
我认为这不是什么重要的或沉重的事情 - 这些图像非常大。像PNG和JPEG这样的图像文件已经被压缩,因此每个图像所需的堆空间将达到数MB。 - CommonsWare
@CommonsWare 我说的是相机图像比那些大20-30倍。感谢您指出了正确的方向。我应该担心这个问题还是忽略它,因为应用程序运行良好。 - Adeel Ahmad
2
“我说过,考虑到相机图像比那些小20-30倍,你也无法一次性将25-30个全分辨率的相机图像加载到内存中。‘应用程序运行良好’——不,你的应用程序正在崩溃,正如你的堆栈跟踪所证明的那样。你需要让Picasso将图像降采样到适合你的GridView的大小,例如在上面显示的代码中链接一个fit()调用。” - CommonsWare
@CommonsWare 哇哇哇。只是添加了 .fit() 就解决了问题,logcat 中不再出现内存不足的情况。此外,RAM 的使用量从 70MB 降至 40MB。很高兴我问了这个问题。 - Adeel Ahmad
也许下面的答案可以帮到你:https://dev59.com/IYHba4cB1Zd3GeqPV879 - yaircarreno
4个回答

27

你原来的代码是下载完整的图像,将完整的图像加载到内存中,然后让Android缩放图像以适应你的ImageView

在这种情况下,你不需要完整的图像在内存中 -- 你需要一个大小更接近于ImageView的东西。

Picassofit()方法处理了这个问题。它使用BitmapFactory.Options上的inSampleSize选项在图像被加载到内存时对其进行降采样,以获得与ImageView大小相近的内容,从而让Android自行缩放。这将显着减少每个图像的内存占用量,特别是取决于你使用多大的ImageView


.fit()并不完美。仅仅调用.fit()比以前更好,但在快速滚动时仍会导致内存异常。使用完全固定的尺寸调用resize()可以完全解决这个问题。 - Adeel Ahmad
1
@AdeelAhmad:嗯...出于某种原因,我认为fit()会执行resize(),但我可能记错了。很高兴它对你有用! - CommonsWare
3
我刚刚检查了源代码,fit()确实会调用resize()函数——如果目标已经有大小,则立即调用;如果目标尚未有大小,则在设置后再调用。 - enl8enmentnow
@CommonsWare,如果调用了fit(),它就会调用。请查看函数RequestCreator.into(target, callback) - Jimit Patel

1

在提到.fit()时,其他答案都很好。

我认为提到.fit()的缺点可能会很有趣:

fit()正在测量目标ImageView的尺寸,并在内部使用resize()将图像大小减小到ImageView的尺寸。有两件事情需要了解fit()。首先,调用fit()可能会延迟图像请求,因为Picasso需要等待直到可以测量ImageView的大小。其次,您只能将fit()与ImageView作为目标一起使用(我们稍后将查看其他目标)。

优点是图像以最低可能的分辨率显示,而不影响其质量。较低的分辨率意味着在缓存中要保存的数据更少。这可以显着降低应用程序的内存占用中图像的影响。总之,如果您更喜欢较低的内存占用而不是稍微快一点的加载时间,则fit()是一个很好的工具。

TLDR:

  • 只能与ImageView目标一起使用
  • 可能会减慢加载速度

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


1

小心使用 .fit()!

最好从服务器提供正确的图像大小,通过将设备屏幕宽度发送到服务器,并为设备提供正确的图像。这样,您就不需要进行缩放。


我猜服务器应该有一组预缩放的图像,以避免过多的额外计算,但这听起来是个好主意。 - Simon Ninon

0

应该像这样

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接