Android:位图recycle()如何工作?


89

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

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

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

myBitmap = BitmapFactory.decodeFile(myFile2);

第一个myBitmap会怎样?它是否已收集垃圾,还是必须在加载另一个位图之前手动进行垃圾收集,例如。 myBitmap.recycle()

另外,有没有更好的方法可以加载大图像并在循环使用的过程中一个接一个地显示它们?

Answers:



23

我认为问题是这样的:在蜂巢之前的Android版本上,实际的原始位图数据不是存储在VM内存中,而是存储在本机内存中。该本机内存当相应的java释放Bitmap对象GC'd。

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

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


21

您将需要在加载下一个图像之前调用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();

3
您不需要调用recycle(),如果您想立即释放内存,这只是一个好主意。
卡鲁2012年

13
接受的答案是“如果要尽快释放内存,则应调用recycle()”。您的答案是“您将需要调用myBitmap.recycle()”。“应该”和“需要”之间有区别,在这种情况下后者是不正确的。
卡鲁2012年

1
上下文很重要。问题是“还存在一种更好的方式来加载大图像并在途中一个接一个地循环显示它们”。
djunod

3
从Android 4.1开始,上述示例可能会中断,因为在某些情况下createScaledBitmap可以返回与原始实例相同的实例。这意味着您必须在回收原始文件之前检查原始文件!= myBitmap。
Jeremyfa

1
@Jeremyfa如果您指定的宽度和高度与原始图像相同,则仅返回原始图像。在那种情况下,缩放是没有意义的,因此跳过它并返回原始图像也可以节省一些过程。它不应该“破坏”任何东西……
Jabari

11

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


+1是一种有趣但很好的方式来描述为什么即时GC无法使用该内存的方法-不错的方法。
理查德·勒·马修里尔

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.