这个用于Bitmaps的inSampleSize计算方法有什么问题?内存不足

时间:2015-05-12 13:25:33

标签: java android bitmap

在我的应用中,我正在迭代图像的网址,解码并将它们放入ArrayList<Bitmap>

它们的大小可能差异很大,因此我使用inJustDecodeBounds = true选项进行“预解码”,以计算实际解码所需的inSampleSize值。

请参阅下面的方法,我希望它不难理解。基本上我的目标是大小与设备的屏幕尺寸相似。

for (Element e: posts) {
    if (!e.id().equals("")) {
        //preparing decode
        options = new BitmapFactory.Options();
        input = new URL(e.url).openStream();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(input, null, options);
        input.close();

        //setting inSampleSize if necessary
        int picPixels = options.outWidth * options.outHeight;
        int picScreenRatio = picPixels / screenPixels;
        if (picScreenRatio > 1) {
            int sampleSize = picScreenRatio % 2 == 0 ? picScreenRatio : picScreenRatio + 1;
            options.inSampleSize = sampleSize;
        }

        //actual decode
        input = new URL(e.url).openStream();
        options.inJustDecodeBounds = false;
        Bitmap pic = BitmapFactory.decodeStream(input, null, options);
        input.close();

        picList.add(pic);
    }
}

计算screenPixels的代码:

Display display = getWindowManager().getDefaultDisplay();
Point screenSize = new Point();
display.getSize(screenSize);
int screenPixels = screenSize.x * screenSize.y;

我正在浏览大约60张图片,大约40张我的应用程序崩溃java.lang.OutOfMemoryError

据我了解,inJustDecodeBounds = true没有分配内存,如果我的方法是正确的(我相信它是),非常大的图像变得非常大inSampleSize s,所以我不知道可能是什么问题。

非常感谢任何建议。

3 个答案:

答案 0 :(得分:2)

样本大小必须是2的幂。来自文档:

  

如果设置为值&gt; 1,请求解码器对原始进行二次采样   图像,返回较小的图像以节省内存。样本量是   任一维度中对应于单个像素的像素数   解码后的位图中的像素。例如,inSampleSize == 4返回一个   图像是原稿宽度/高度的1/4,和1/16   像素数。任何值&lt; = 1都被视为1.注意:   解码器使用基于2的幂的最终值,任何其他值都将   向下舍入到最接近2的幂。

这是Android - Volley库中关于如何为位图找到最佳样本大小的好方法:

    int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
        double wr = (double) actualWidth / desiredWidth;
        double hr = (double) actualHeight / desiredHeight;
        double ratio = Math.min(wr, hr);
        float n = 1.0f;
        while ((n * 2) <= ratio) {
            n *= 2;
        }

        return (int) n;
    }

答案 1 :(得分:2)

无论您使用inSampleSize,都无法轻松保存与内存中屏幕一样大的60位图。

假设有一个全高清屏幕,你会得到1920*1080*60*4个字节。

大概 500 MB的数据。 您需要以某种方式更改位图加载逻辑。

this回答中,您可以了解可以计算多少内存。它因各种设备而异。

但总是尽量让你的应用程序尽可能保持内存效率。

答案 2 :(得分:0)

在Android上正确处理图像处理是非常困难的。我建议您使用已建立的图像管理库,如Facebook's Fresco

https://github.com/facebook/fresco

他们的文档中的一个例子:

  

Fresco的Drawees为您显示占位符,直到图像加载并在图像到达时自动显示。当图像离开屏幕时,它会自动释放内存。

此外:

  

使用Fresco的应用程序甚至可以在低端设备上运行,而无需经常努力控制图像内存占用。