处理大的位图
我有一堆图片url。 我必须下载这些图像并逐个显示在我的应用程序中。 我使用SoftReferences
和Sdcard将图像保存在一个集合中,以避免重读和改善用户体验。
问题是我不知道有关位图的大小。 事实certificate,当我使用BitmapFactory.decodeStream(InputStream)
方法时,我偶尔会发生OutOfMemoryException BitmapFactory.decodeStream(InputStream)
。 所以,我select使用BitmapFactory选项(sample size = 2)对图像进行缩减采样。 这给了更好的输出:没有OOM,但是这会影响较小图像的质量。
我应该如何处理这种情况? 有没有办法有select地降低采样高分辨率的图像?
BitmapFactory.Options
类中有一个选项(我忽略了一个),名为inJustDecodeBounds
,其中的javadoc的内容如下:
如果设置为true,解码器将返回null(无位图),但out …字段仍将被设置,允许调用者查询位图而不必为其像素分配内存。
我用它来查找位图的实际大小,然后select使用inSampleSize
选项向下采样。 这至less可以在解码文件时避免任何OOM错误。
参考:
1. 处理较大的位图
2. 解码之前如何获取位图信息?
我自己做的是:
- 使用
inJustDecodeBounds
来获取图像的原始大小 - 有一个固定的最大曲面用于加载位图(比如1M像素)
- 检查图像表面是否低于极限,如果是,则直接加载
- 如果不计算理想的宽度和高度,在这个宽度和高度上你应该加载位图,使其保持在最大表面之下(这里有一些简单的math运算)。 这给你一个你想在加载位图时应用的浮点率
- 现在你想把这个比例转换成合适的
inSampleSize
(2的幂次不会降低图像质量)。 我使用这个function:
int k = Integer.highestOneBit((int)Math.floor(ratio)); 如果(k == 0)返回1; 否则返回k;
- 那么因为Bitmap已经被加载了比最大表面稍高的分辨率(因为你必须使用较小的2的幂),所以你将不得不调整位图的大小,但是它会快得多。
经过几天的努力,以避免我用不同的设备获得的所有OutOfMemory错误,我创build这个:
private Bitmap getDownsampledBitmap(Context ctx, Uri uri, int targetWidth, int targetHeight) { Bitmap bitmap = null; try { BitmapFactory.Options outDimens = getBitmapDimensions(uri); int sampleSize = calculateSampleSize(outDimens.outWidth, outDimens.outHeight, targetWidth, targetHeight); bitmap = downsampleBitmap(uri, sampleSize); } catch (Exception e) { //handle the exception(s) } return bitmap; } private BitmapFactory.Options getBitmapDimensions(Uri uri) throws FileNotFoundException, IOException { BitmapFactory.Options outDimens = new BitmapFactory.Options(); outDimens.inJustDecodeBounds = true; // the decoder will return null (no bitmap) InputStream is= getContentResolver().openInputStream(uri); // if Options requested only the size will be returned BitmapFactory.decodeStream(is, null, outDimens); is.close(); return outDimens; } private int calculateSampleSize(int width, int height, int targetWidth, int targetHeight) { int inSampleSize = 1; if (height > targetHeight || width > targetWidth) { // Calculate ratios of height and width to requested height and // width final int heightRatio = Math.round((float) height / (float) targetHeight); final int widthRatio = Math.round((float) width / (float) targetWidth); // Choose the smallest ratio as inSampleSize value, this will // guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } private Bitmap downsampleBitmap(Uri uri, int sampleSize) throws FileNotFoundException, IOException { Bitmap resizedBitmap; BitmapFactory.Options outBitmap = new BitmapFactory.Options(); outBitmap.inJustDecodeBounds = false; // the decoder will return a bitmap outBitmap.inSampleSize = sampleSize; InputStream is = getContentResolver().openInputStream(uri); resizedBitmap = BitmapFactory.decodeStream(is, null, outBitmap); is.close(); return resizedBitmap; }
这种方法适用于我testing过的所有设备,但是我认为使用其他不知道的stream程可以提高质量。
我希望我的代码可以帮助其他开发人员在相同的情况下。 我也很感激一位高级开发人员能否提供帮助,并提出一个关于其他程序的build议,以避免在这个过程中丢失(less)质量。