毕加索:内存不足 [英] Picasso: out of memory

查看:106
本文介绍了毕加索:内存不足的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个RecyclerView,使用Picasso呈现了几张图像.在向上和向下滚动一段时间后,该应用程序将耗尽内存,并显示以下消息:

I have a RecyclerView presenting several images using Picasso. After scrolling some time up and down the application runs out of memory with messages like this:

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

我在调试时注意的事情:

The things I note while debugging:

  1. 在手机或虚拟设备上安装应用程序时,图像将通过网络加载,这就是本意.通过图像左上角的红色三角形可以看到这一点.
  2. 滚动以重新加载图像时,将从磁盘中获取图像.通过图像左上角的蓝色三角形可以看到这一点.
  3. 进一步滚动时,某些图像会从内存中加载,如左上角的绿色三角形所示.
  4. 滚动更多内容后,将发生内存不足异常,并且加载将停止.当前未保存在内存中的图像上仅显示占位符图像,而内存中的占位符则正确显示为绿色三角形.

此处是示例图片.它很大,但是我使用fit()来减少应用程序中的内存占用.

Here is a sample image. It is quite large, but I use fit() to reduce the memory footprint in the app.

所以我的问题是:

  • 在内存缓存中不应该从磁盘重新加载图像 满了吗?
  • 图像是否太大?我可以有多少内存 预计解码后会消耗0.5 MB的图像吗?
  • 下面的代码中有什么错误/异常之处吗?
  • Shouldn't the images be reloaded from disk when the memory cache is full?
  • Are the images just too large? How much memory can I expect an, let's say 0.5 MB image, to consume when decoded?
  • Is there anything wrong/unusual in my code below?

在创建Activity时设置静态毕加索实例:

Setting up the static Picasso instance when creating the Activity:

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中使用静态毕加索实例:

Using the static Picasso instance in my RecyclerView.Adapter:

@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:

The ImageView in the XML file:

<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,以匹配官方文档,将单个图像与ImageView一起使用200个CardView,但是问题仍然存在.大多数图像是从内存中加载的(绿色),并且滚动平滑,但是大约十分之一ImageView是从磁盘中加载的图像(蓝色).从磁盘加载映像时,将执行内存分配,从而增加了堆上的分配,从而增加了堆本身.

Update

It seems that scrolling the RecyclerView continuously makes the memory allocation increase indefinitely. I made a test RecyclerView stripped down to match the official documentation, using a single image for 200 CardViews with an ImageView, but the problem persists. Most of the images are loaded from memory (green) and scrolling is smooth, but about every tenth ImageView loads the image from disk (blue). When the image is loaded from disk, a memory allocation is performed, thereby increasing the allocation on the heap and thus the heap itself.

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

I tried removing my own setup of the global Picasso instance and using the default instead, but the problems are the same.

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

I did a check with the Android Device Monitor, see the image below. This is for a Galaxy S3. Each of the allocations done when an image is loaded from disk can be seen to the right under "Allocation count per size". The size is slightly different for each allocation of the image, which is also weird. Pressing "Cause GB" makes the rightmost allocation of 4.7 MB go away.

对于虚拟设备,其行为是相同的.下图显示了Nexus 5 AVD的图片.同样在这里,按"Cause GB"时,最大的分配(10.6 MB的分配)消失了.

The behavior is the same for virtual devices. The image below shows it for a Nexus 5 AVD. Also here, the largest allocation (the 10.6 MB one) goes away when pressing "Cause GB".

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

Additionally, here are images of the memory allocation locations and the threads from the Android Device Monitor. The reoccurring allocations are done in the Picasso threads while the one removed with Cause GB is done on the main thread.

推荐答案

我不确定fit()是否可以与android:adjustViewBounds="true"一起使用.根据某些过去的问题看来,这是有问题的.

I'm not sure fit() works with android:adjustViewBounds="true". According to some of the past issues it seems to be problematic.

一些建议:

  • 为ImageView设置固定大小
  • 使用 GlobalLayoutListener 的用户获取ImageView的大小是计算出来的,在此调用之后,毕加索添加了resize()方法
  • 尝试使用 Glide -其默认配置所产生的占用空间比Picasso少(它存储了调整后的大小图像而不是原始图像,并使用RGB565)
  • Set a fixed size for the ImageView
  • User a GlobalLayoutListener to get the ImageView's size once it is calculated and after this call Picasso adding the resize() method
  • Give Glide a try - its default configuration results in a lower footprint than Picasso (it stores the resized imaged rather than the original and uses RGB565)

这篇关于毕加索:内存不足的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆