【问题标题】:Android - Bitmap cache takes a lot of memoryAndroid - 位图缓存占用大量内存
【发布时间】:2013-08-17 19:30:25
【问题描述】:

我是所有内存管理主题的新手,所以有很多东西我不明白。
我正在尝试在我的应用中缓存图像,但我遇到了内存消耗问题:

所有的 Bitmap Chaching 代码几乎都是从这里复制粘贴的:http://developer.android.com/training/displaying-bitmaps/index.html

我在eclipse的DDMS视图中调试了代码并检查了堆大小,在这些代码行之后大约有15mb的跳转:

        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);

在“decodeSampledBitmapFromResource”方法中。

图片为 1024x800、75kb jpg 文件。根据我在网上看到的,这张图片应该占用的内存大约是 1024*800*4(Bytes per pixel)=3.125mb

关于这个主题的所有线程都没有说明为什么它占用的内存比它应该的多得多。有没有办法用合理的内存缓存一张图片?

编辑

我尝试使用下面@ArshadParwez 的回答中建议的 decodeFile 方法。使用这种方法,在 BitmapFactory.decodeStream 方法之后,内存只增加了 3.5mb - 问题解决了,有点,但我想直接从资源中缓存位图。

我注意到,在 decodeResource 方法期间,有 2 次内存“跳跃”——其中一次约为 3.5mb——这是合理的,另一次奇怪的是 14mb。那些 14mb 是用来做什么的?为什么会这样?

【问题讨论】:

  • 缓存到文件系统而不是活动内存怎么样?
  • @PaulNikonowicz 即使解决了问题,也可以推测辅助内存是闪存或类似类型,频繁写入会缩短其寿命,而主内存则不然。
  • 4 个字节用于 ARGB4444。你确定不是argb8888?
  • @DiegoCNascimento 二级内存并不一定意味着带有 Android 设备的闪存卡。
  • 即使是8字节也应该是6.25mb。而且我不想避免缓存,这不是解决方案。必须有正确的方法。

标签: java android caching bitmap


【解决方案1】:

您可以使用此方法传递图像并从中获取位图:

public Bitmap decodeFile(File f) {
    Bitmap b = null;
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        BitmapFactory.decodeStream(fis, null, o);
        fis.close();
        int IMAGE_MAX_SIZE = 1000;
        int scale = 1;
        if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
            scale = (int) Math.pow(
                    2,
                    (int) Math.round(Math.log(IMAGE_MAX_SIZE
                            / (double) Math.max(o.outHeight, o.outWidth))
                            / Math.log(0.5)));
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        b = BitmapFactory.decodeStream(fis, null, o2);
        fis.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return b;
}

【讨论】:

  • 我在处理图像时也遇到了很多崩溃,比如 OutOfMemoryError。所以我用一些数学方法做了这个方法,之后我可以使用 5mb 大小的图像而不会崩溃。
  • 在我尝试这个之前,我想了解原因 - 你是说 BitmapFactory.decodeStream 解码比 BitmapFactory.decodeResource 更好?
  • 不是这样,我使用了 decodeStream,因为我将图像文件作为输入并使用 FileInputStream,如果它是资源文件,那么我会使用 decodeResource
  • 那么这比 developer.android.com/training/displaying-bitmaps/… 所示的 decodeSampledBitmapFromResource 好多少?
  • 该链接中的方法和我使用的方法不同,因为我将图像的最大尺寸固定为 1000 像素,此外,如果您使用我的代码,您将看不到输出图像有太多降级,而该链接处的代码会大大降低输出图像质量。
【解决方案2】:

@Ori Wasserman:根据您的要求,我使用了一种从资源文件夹中获取图像的方法,并且我也使用了 7 MB 的图像。我将 7 MB 图像放在“res->drawable”文件夹中,使用以下代码它没有崩溃,图像显示在 imageview 中:

 Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.image_7mb);
 loBitmap = Bitmap.createScaledBitmap(image, width_of_screen , height_of_screen, true);
 imageview.setImageBitmap(loBitmap);

【讨论】:

  • 实际上它会使用比你说的更多的内存,只是在短时间内。原因是您创建了原始位图和一个小的位图。您不会注意到它,因为大图像将在下一次 GC 中处理掉。两者兼得是最关键的一点。
  • 您正在创建 2 个位图来实现 1 个单一目标,这对记忆来说非常令人沮丧。尝试在此问题中使用 BitmapFactory.Option。
【解决方案3】:

图像也会根据密度进行缩放,因此它们可以使用大量内存。

例如,如果图像文件位于drawable 文件夹中(即mdpi 密度)并且您在xhdpi 设备上运行它,则宽度和高度都会加倍。也许this link 可以帮助你,或者this one

因此,在您的示例中,图像文件将占用的字节数是:

(1024*2)*(800*2)*4 = 13,107,200 字节。

如果您在xxhdpi 设备(如 HTC one 和 Galaxy S4)上运行它会更糟。

你能做什么?将图像文件放入正确的密度文件夹(drawable-xhdpidrawable-xxhdpi)或放入 drawable-nodpi(或 assets 文件夹)并根据需要缩小图像。

顺便说一句,您不必设置options.inJustDecodeBounds = false,因为它是默认行为。事实上,您可以将位图选项设置为 null。

关于缩小比例,您可以使用google's waymy way,它们各有优缺点。

关于缓存,有很多方法可以做到这一点。最常见的一种是 LRU 缓存。我最近还创建了一个替代方案(链接herehere),它允许您缓存更多图像并避免出现OOM,但它会给您带来很多责任。

【讨论】:

  • 好的。我忘记了关于位图的另一个提示:您可以设置它们的格式,所以如果您不需要透明度并且质量并不重要,您可以使用 RGB_565(每像素使用 2 个字节)而不是默认的 ARGB_8888(使用 4每个像素的字节数)。链接在这里:developer.android.com/reference/android/graphics/…。甚至在 google IO 网站上也有一些关于内存和位图的视频。我建议观看他们,即使他们没有谈论您的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
  • 2014-05-11
  • 2011-02-27
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多