大多数内存有效的方法来重新调整android上的位图?

时间:2015-08-20 14:31:51

标签: android performance bitmap out-of-memory

我正在构建一个图像密集型社交应用,其中图像从服务器发送到设备。当设备具有较小的屏幕分辨率时,我需要调整设备上的位图大小以匹配其预期的显示大小。

问题是使用 createScaledBitmap 会导致我在调整大量缩略图图像后遇到大量内存不足错误。

在Android上调整位图大小的最有效内存方法是什么?

2 个答案:

答案 0 :(得分:161)

  

此答案摘自Loading large bitmaps Efficiently   这解释了如何使用inSampleSize加载缩小的位图   版本

     

特别是Pre-scaling bitmaps解释了各种细节   方法,如何组合它们,哪些是最有效的内存。

在Android上调整具有不同内存属性的位图有三种主要方法:

createScaledBitmap API

此API将采用现有位图,并使用您选择的确切尺寸创建新位图。

从好的方面来说,你可以准确地得到你正在寻找的图像尺寸(无论它看起来如何)。但缺点是,是这个API需要现有的位图才能工作。这意味着在创建新的较小版本之前,必须加载,解码图像并创建位图。这在获得精确尺寸方面是理想的,但在额外的内存开销方面却很糟糕。因此,对于大多数倾向于记忆的应用程序开发人员而言,这是一种交易破坏者

<强> inSampleSize flag

BitmapFactory.Options有一个标记为inSampleSize的属性会在解码时调整图像大小,以避免需要解码到临时位图。此处使用的此整数值将以1 / x缩小的尺寸加载图像。例如,将inSampleSize设置为2将返回尺寸减半的图像,将其设置为4将返回尺寸为1/4的图像。基本上,图像尺寸总是比源尺寸小一些。

从内存的角度来看,使用inSampleSize是一个非常快速的操作。实际上,它只会将图像的每个第X个像素解码为生成的位图。但inSampleSize存在两个主要问题:

  • 它没有给出确切的分辨率。它只会将位图的大小减小2倍。

  • 它不会产生最佳质量调整大小。大多数调整大小的滤镜通过读取像素块产生漂亮的图像,然后对它们进行加权以产生所讨论的调整大小的像素。 inSampleSize只需读取每几个像素就可以避免这一切。结果非常高效,内存不足,但质量却受到影响。

如果您只是处理以某种pow2尺寸缩小图像,并且过滤不是问题,那么您找不到比inSampleSize更高效的内存(或性能效率)方法。< / p>

<强> inScaled, inDensity, inTargetDensity flags

如果您需要将图片缩放到不等于2的幂,那么您需要{{1}的inScaledinDensityinTargetDensity标记}}。设置BitmapOptions标志后,系统会通过将inScaled除以inTargetDensity值来派生缩放值以应用于您的位图。

inDensity

使用此方法会重新调整图像大小,并对其应用“调整大小过滤器”,也就是说,最终结果看起来会更好,因为在调整大小步骤期间会考虑一些额外的数学运算。但要注意:额外的过滤步骤,需要额外的处理时间,并且可以快速添加大图像,导致调整缓慢,并为过滤器本身额外的内存分配。

由于额外的过滤开销,将此技术应用于远大于所需大小的图像通常不是一个好主意。

魔术组合

从内存和性能角度来看,您可以将这些选项组合在一起以获得最佳效果。 (设置mBitmapOptions.inScaled = true; mBitmapOptions.inDensity = srcWidth; mBitmapOptions.inTargetDensity = dstWidth; // will load & resize the image to be 1/inSampleSize dimensions mCurrentBitmap = BitmapFactory.decodeResources(getResources(), mImageIDs, mBitmapOptions); inSampleSizeinScaledinDensity标记)

inTargetDensity将首先应用于图像,使其达到下一个比目标尺寸更大的两倍。然后,inSampleSize&amp; inDensity用于将结果缩放到所需的精确尺寸,应用滤镜操作来清理图像。

将这两者结合起来是一个快得多的操作,因为inTargetDensity步骤将减少基于密度的步骤所需的像素数,以应用它的调整大小过滤器。

inSampleSize

如果您需要将图像适合特定尺寸,一些更好的过滤,那么这种技术是获得正确尺寸的最佳桥梁,但是在快速,低内存中完成足迹操作。

获取图片尺寸

获取图像大小而不解码整个图像 要调整位图大小,您需要知道传入的尺寸。您可以使用mBitmapOptions.inScaled = true; mBitmapOptions.inSampleSize = 4; mBitmapOptions.inDensity = srcWidth; mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize; // will load & resize the image to be 1/inSampleSize dimensions mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions); 标记来帮助您获取图像的尺寸,而无需实际解码像素数据。

inJustDecodeBounds

您可以使用此标志首先解码大小,然后计算适当的值以缩放到目标分辨率。

答案 1 :(得分:12)

尽管这个答案很好(也很准确),但它也很复杂。而不是重新发明轮子,考虑像GlidePicassoUILIon或其他任何为您实现这种复杂且容易出错的逻辑的库。 。

柯尔特本人甚至建议在Pre-scaling Bitmaps Performance Patterns Video中看看格莱德和毕加索。

通过使用库,您可以获得Colt答案中提到的所有效率,但是使用非常简单的API可以在每个Android版本中保持一致。