android - picasso :失忆

标签 android out-of-memory picasso

我有一个 RecyclerView 使用 Picasso 呈现多个图像。上下滚动一段时间后,应用程序内存不足并显示如下消息:

E/dalvikvm-heap﹕ Out of memory on a 3053072-byte allocation.
I/dalvikvm﹕ "Picasso-/wp-content/uploads/2013/12/DSC_0972Small.jpg" prio=5 tid=19 RUNNABLE
I/dalvikvm﹕ | group="main" sCount=0 dsCount=0 obj=0x42822a50 self=0x59898998
I/dalvikvm﹕ | sysTid=25347 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1500612752
I/dalvikvm﹕ | state=R schedstat=( 10373925093 843291977 45448 ) utm=880 stm=157 core=3
I/dalvikvm﹕ at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
I/dalvikvm﹕ at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:623)
I/dalvikvm﹕ at com.squareup.picasso.BitmapHunter.decodeStream(BitmapHunter.java:142)
I/dalvikvm﹕ at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:217)
I/dalvikvm﹕ at com.squareup.picasso.BitmapHunter.run(BitmapHunter.java:159)
I/dalvikvm﹕ at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
I/dalvikvm﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:234)
I/dalvikvm﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
I/dalvikvm﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
I/dalvikvm﹕ at java.lang.Thread.run(Thread.java:841)
I/dalvikvm﹕ at com.squareup.picasso.Utils$PicassoThread.run(Utils.java:411)
I/dalvikvm﹕ [ 08-10 18:48:35.519 25218:25347 D/skia     ]
    --- decoder->decode returned false

调试时注意的事项:

  1. 在手机或虚拟设备上安装应用时,图像会通过网络加载,这就是它的本意。这可以通过图片左上角的红色三角形看到。
  2. 当滚动以重新加载图像时,它们会从磁盘中获取。这可以通过图片左上角的蓝色三角形看到。
  3. 再滚动一些时,一些图像会从内存中加载,如左上角的绿色三角形所示。
  4. 再滚动一段时间后,出现内存不足异常并停止加载。当前未保存在内存中的图像上仅显示占位符图像,而内存中的图像以绿色三角形正确显示。

Here是一个示例图像。它相当大,但我使用 fit() 来减少应用程序中的内存占用。

所以我的问题是:

  • 内存缓存时不应该从磁盘重新加载图像吗 满了吗?
  • 图片是否太大?我能有多少内存 期望在解码时消耗一个(比方说 0.5 MB 的图像)?
  • 下面的代码有什么错误/不寻常的地方吗?

在创建 Activity 时设置静态 Picasso 实例:

private void setupPicasso()
{
    Cache diskCache = new Cache(getDir("foo", Context.MODE_PRIVATE), 100000000);
    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.setCache(diskCache);

    Picasso picasso = new Picasso.Builder(this)
            .memoryCache(new LruCache(100000000)) // Maybe something fishy here?
            .downloader(new OkHttpDownloader(okHttpClient))
            .build();

    picasso.setIndicatorsEnabled(true); // For debugging

    Picasso.setSingletonInstance(picasso);
}

在我的 RecyclerView.Adapter 中使用静态 Picasso 实例:

@Override
public void onBindViewHolder(RecipeViewHolder recipeViewHolder, int position)
{
    Picasso.with(mMiasMatActivity)
            .load(mRecipes.getImage(position))
            .placeholder(R.drawable.picasso_placeholder)
            .fit()
            .centerCrop()
            .into(recipeViewHolder.recipeImage); // recipeImage is an ImageView

    // More...
}

XML文件中的ImageView:

<ImageView
    android:id="@+id/mm_recipe_item_recipe_image"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:paddingBottom="2dp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:clickable="true"
/>

更新

似乎连续滚动RecyclerView会使内存分配无限增加。我做了一个测试 RecyclerView 被剥离以匹配 official documentation ,将单个图像用于 200 个 CardViewImageView,但问题仍然存在。大多数图像是从内存中加载的(绿色)并且滚动很流畅,但大约每十分之一 ImageView 从磁盘加载图像(蓝色)。当从磁盘加载图像时,会执行内存分配,从而增加堆上的分配,从而增加堆本身。

我尝试删除自己的全局 Picasso 实例设置并改用默认设置,但问题是一样的。

我使用 Android 设备监视器进行了检查,请参见下图。这是针对 Galaxy S3 的。从磁盘加载图像时完成的每个分配都可以在“每个大小的分配计数”下看到。每次分配图像的大小都略有不同,这也很奇怪。按“Cause GB”会使最右边的 4.7 MB 分配消失。

Android Device Monitor

虚拟设备的行为相同。下图显示了 Nexus 5 AVD。同样在这里,当按下“Cause GB”时,最大的分配(10.6 MB)消失了。

Android Device Monitor

此外,这里是内存分配位置和 Android 设备监视器中的线程的图像。重复发生的分配在 Picasso 线程中完成,而用 Cause GB 删除的分配在主线程中完成。

Allocation Tracker Threads

最佳答案

我不确定 fit() 是否适用于 android:adjustViewBounds="true"。根据一些 past issues好像有问题。

一些建议:

  • 为 ImageView 设置固定大小
  • 用户 a GlobalLayoutListener在计算出 ImageView 的大小后,在调用 Picasso 之后添加 resize() 方法
  • Glide试一试 - 它的默认配置比 Picasso 占用更少的空间(它存储调整大小的图像而不是原始图像,并使用 RGB565)

关于android - picasso :失忆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31925461/

相关文章:

android - 琐碎的驱动器构建失败

android - 如何用android中图像下方的内容填充空白空间

filter - 如何避免 ffmpeg 内存不足错误

使用 random.sample() 时出现 Python 内存错误

java - 从 Picasso 保存到 Drawable

java - "Camera Error 1004"使用Android相机拍照时出错

java - 如何从发布构建中排除模块

android - java.lang.OutOfMemoryError : bitmap size exceeds VM budget in ListView and lazy loading images 错误

android - java.lang.ClassNotFoundException : Didn't find class "com.squareup.picasso.Picasso" 异常

java - Picasso context==null 问题