我正在构建一个图像密集型社交应用程序,其中图像从服务器发送到设备。当设备的屏幕分辨率较小时,我需要在设备上调整位图的大小,以匹配其预期的显示大小。
问题是使用createScaledBitmap导致我在调整成组的缩略图图像后遇到很多内存不足错误。
在Android上调整位图大小的最有效内存方式是什么?
我正在构建一个图像密集型社交应用程序,其中图像从服务器发送到设备。当设备的屏幕分辨率较小时,我需要在设备上调整位图的大小,以匹配其预期的显示大小。
问题是使用createScaledBitmap导致我在调整成组的缩略图图像后遇到很多内存不足错误。
在Android上调整位图大小的最有效内存方式是什么?
Answers:
从“有效加载大型位图”中可以总结出此答案 ,该说明说明了如何使用inSampleSize加载缩小的位图版本。
特别是,预缩放位图详细说明了各种方法,如何组合它们以及哪种方法最节省内存。
在Android上,三种主要的调整位图大小的方法具有不同的内存属性:
该API将采用现有位图,并使用您选择的确切尺寸创建新的位图。
从好的方面来说,您可以确切地找到想要的图像大小(无论外观如何)。但是缺点是,此API需要一个现有的位图才能起作用。这意味着必须先加载,解码图像并创建位图,然后才能创建新的较小版本。就获得确切的尺寸而言,这是理想的选择,但在额外的内存开销方面却是可怕的。因此,对于大多数倾向于内存意识的应用程序开发人员来说,这是一个交易突破点
BitmapFactory.Options
具有指出的属性,inSampleSize
该属性将在解码图像时调整图像的大小,以避免需要解码为临时位图。此处使用的此整数值将以缩小后的1 / x大小加载图像。例如,设置inSampleSize
为2将返回图像大小的一半,将其设置为4将返回图像大小的1/4。基本上,图像尺寸总是比源尺寸小2的幂。
从内存的角度来看,使用inSampleSize
是一个非常快的操作。实际上,它只会将图像的每个第X个像素解码为最终的位图。inSampleSize
尽管存在两个主要问题:
它没有给您确切的分辨率。它只会将位图的大小减小2的幂。
它不会产生最佳的质量调整大小。大多数调整大小的滤镜会通过读取像素块,然后对其进行加权以生成所讨论的调整大小的像素来生成美观的图像。inSampleSize
仅读取每几个像素就可以避免所有这些情况。结果是相当不错的性能,并且内存不足,但是质量受到了影响。
如果您只想将图片缩小至pow2大小,而过滤不是问题,那么您找不到比更高的内存效率(或性能效率)方法inSampleSize
。
inScaled,inDensity,inTargetDensity标志
如果您需要将图像缩放到,这不是等于二的幂的尺寸,那么你需要的inScaled
,inDensity
和inTargetDensity
旗帜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);
使用此方法将调整图像的大小,并对其应用“调整大小的滤镜”,即最终结果看起来会更好,因为在调整大小步骤中已考虑了一些附加的数学运算。但请注意:该额外的过滤器步骤会占用更多的处理时间,并且会迅速增加大图像,导致调整大小缓慢,并为过滤器本身分配了额外的内存。
通常,将这种技术应用于明显大于所需大小的图像不是一个好主意,因为存在额外的过滤开销。
魔术组合
从内存和性能的角度来看,您可以组合使用这些选项以获得最佳效果。(设置inSampleSize
,inScaled
,inDensity
和inTargetDensity
标志)
inSampleSize
首先将其应用于图像,使其达到目标尺寸的下一个2幂。然后,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
标志来帮助您获取图像的尺寸,而无需实际解码像素数据。
// Decode just the boundaries
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;
//now go resize the image to the size you want
您可以使用此标志先解码大小,然后计算适当的值以缩放到目标分辨率。
destination width
或dstWidth的简称