Android:位图回收()如何工作?

Posted

技术标签:

【中文标题】Android:位图回收()如何工作?【英文标题】:Android: How does Bitmap recycle() work? 【发布时间】:2011-04-18 22:29:43 【问题描述】:

假设我已经在位图对象中加载了一个图像

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

现在,如果我加载另一个位图会发生什么

myBitmap = BitmapFactory.decodeFile(myFile2);

第一个 myBitmap 会发生什么?它是垃圾收集还是我必须在加载另一个位图之前手动垃圾收集它,例如。 myBitmap.recycle()?

另外,有没有更好的方法来加载大图并在途中回收时一张张显示?

【问题讨论】:

【参考方案1】:

当您解码第二个位图时,第一个位图不是garbage collected。 Garbage Collector 将在以后决定时执行此操作。如果你想尽快释放内存,你应该在解码第二个位图之前调用recycle()

如果你想加载非常大的图像,你应该重新采样它。这是一个示例:Strange out of memory issue while loading an image to a Bitmap object。

【讨论】:

【参考方案2】:

我认为问题在于:在 Honeycomb 之前的 android 版本中,实际的原始位图数据并未存储在 VM 内存中,而是存储在本机内存中。当对应的 java Bitmap 对象被 GC'd 时,此本机内存释放。

然而,当你用完本机内存时,dalvik GC 不会被触发,所以你的应用程序可能会使用很少的 java 内存,因此永远不会调用 dalvik GC ,但它使用大量本机内存来存储位图,最终导致 OOM 错误。

至少这是我的猜测。值得庆幸的是,在 Honeycomb 及更高版本中,所有位图数据都存储在 VM 中,因此您根本不必使用 recycle()。但是对于数以百万计的 2.3 用户(碎片握拳),您应该尽可能使用recycle()(非常麻烦)。或者,您也可以调用 GC。

【讨论】:

【参考方案3】:

您需要在加载下一张图片之前调用 myBitmap.recycle()。

根据您的 myFile 的来源(例如,如果它是您无法控制原始大小的东西),当加载图像而不是简单地重新采样某个任意数字时,您应该将图像缩放到显示大小。

if (myBitmap != null) 
    myBitmap.recycle();
    myBitmap = null;

Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

我将 displayWidth 和 displayHeight 缓存在我在 Activity 开始时初始化的静态文件中。

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();

【讨论】:

你不需要调用recycle(),如果你想马上释放内存是个好主意。 接受的答案是“如果你想尽快释放内存,你应该调用 recycle()”。您的回答是“您需要调用 myBitmap.recycle()”。 “应该”和“需要”是有区别的,在这种情况下后者是不正确的。 上下文很重要。问题是“还有更好的方法来加载大图像并在途中回收显示它们”。 从 Android 4.1 开始,上述示例可能会中断,因为在某些情况下 createScaledBitmap 可以返回与原始实例相同的实例。这意味着您必须在回收原件之前检查原件!= myBitmap。 @Jeremyfa 如果您指定的宽度和高度与原始图像相同,它只会返回原始图像。在这种情况下,缩放是没有意义的,因此它还可以通过跳过它并返回原始图像来节省一些过程。它不应该“破坏”任何东西......【参考方案4】:

一旦位图被加载到内存中,实际上它是由两部分数据组成的。 第一部分包括一些关于位图的信息,另一部分包括关于位图像素的信息(它由字节数组组成)。 第一部分存在于 Java 已用内存中,第二部分存在于 C++ 已用内存中。可以直接使用对方的内存。 Bitmap.recycle() 用于释放 C++ 的内存。 如果你只这样做,GC会收集java的部分,并且总是使用C的内存。

【讨论】:

+1 是一种有趣但非常好的方式来描述为什么内存不能用于立即 GC - 很好。【参考方案5】:

Timmmm 是对的。

根据: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

此外,在 Android 3.0(API 级别 11)之前,位图的支持数据存储在本机内存中,不会以可预测的方式释放,可能会导致应用程序短暂超出其内存限制和崩溃。

【讨论】:

以上是关于Android:位图回收()如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

Android内存优化1-对Bitmap的内存优化

Canvas:尝试在 Android 2.3 上使用回收的位图 RuntimeException

调整可绘制图层列表内的位图大小 2

打开相机意图时如何防止android应用程序方向

Android:我需要拍照并将其存储在 sqlite 数据库中,然后将其填充到回收站视图中

在可绘制图层列表中调整位图大小