Android Bitmap.createScaledBitmap在Jelly Bean 4.1上经常抛出java.lang.OutOfMemoryError异常

11

我的应用程序的主要目的是以图像中显示的方式显示图像,如下所示:

enter image description here

private void setSelectedImage(int selectedImagePosition) 
{

    BitmapDrawable bd = (BitmapDrawable) drawables.get(selectedImagePosition);
    Bitmap b = Bitmap.createScaledBitmap(bd.getBitmap(), (int) (bd.getIntrinsicHeight() * 0.9), (int) (bd.getIntrinsicWidth() * 0.7), false);
    selectedImageView.setImageBitmap(b);
    selectedImageView.setScaleType(ScaleType.FIT_XY);

}

详细代码可以在这里找到。

异常发生在以下行:

Bitmap b = Bitmap.createScaledBitmap(bd.getBitmap(), (int) (bd.getIntrinsicHeight() * 0.9), (int) (bd.getIntrinsicWidth() * 0.7), false);

上面的函数被从onItemSelected中调用。该应用在2.2和2.3上仍能正常工作,但在4.1上立即抛出异常。上述代码运行良好,但会抛出以下异常。我没有看到2.2和2.3中的任何崩溃,但它在4.1中立即崩溃。在Jelly Bean中是否存在主要的内存管理差异?
java.lang.OutOfMemoryError
AndroidRuntime(2616):   at android.graphics.Bitmap.nativeCreate(Native Method)
AndroidRuntime(2616):   at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
AndroidRuntime(2616):   at android.graphics.Bitmap.createBitmap(Bitmap.java:586) 
AndroidRuntime(2616):   at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:466)
AndroidRuntime(2616):   at com.rdx.gallery.GalleryDemoActivity.setSelectedImage(GalleryDemoActivity.java:183)

你的位图宽度和高度是多少? - Göksel Güren
我认为问题出在 selectedImageView.setScaleType(ScaleType.FIT_XY);。 - Göksel Güren
@GökselGüren,问题不在于ScaleType,而在于CreateScaledBitmap。 - Rohit
@talhakosen 因为在缩放操作中,需要分配大量内存空间。但我不清楚 selectedImageView.setScaleType(ScaleType.FIT_XY) 函数的算法。 - Göksel Güren
@Goksel,据我所知,setScale函数不需要太多内存,但从错误的资源(例如xlarge)进行缩放可能会导致内存异常。 - Talha
显示剩余2条评论
3个回答

25

需要注意的是,以下代码可能会引发异常:

Bitmap bitmap = Bitmap.createScaledBitmap(oldBitmap, newWidth, newHeight, true); 
oldBitmap.recycle();

正确的写法是:

Bitmap bitmap = Bitmap.createScaledBitmap(oldBitmap, newWidth, newHeight, true); 
if (oldBitmap!= bitmap){
     oldBitmap.recycle();
}

因为文档说明:

如果指定的宽度和高度与源位图的当前宽度和高度相同,则返回源位图,并且不创建新的位图。


如果位图已经因任何原因被回收,此代码仍然可能触发异常,您需要将以下内容添加到检查中: &&!oldBitmap.isRecycled() - Yoann Hercouet
在Bitmap类的哪个实现中需要这个方法?因为在5.1.0_r1版本中并不需要,因为该方法会检查自身:“if (!mRecycled && mFinalizer.mNativeBitmap != 0) ”。 http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.0_r1/android/graphics/Bitmap.java#Bitmap.recycle%28%29 - Malachiasz

13

http://www.youtube.com/watch?v=_CruQY55HOk。在安卓3.0之后,位图像素数据存储在堆上。看起来你正在超出堆内存大小。仅仅因为你的应用需要大的堆内存并不意味着要使用大的堆内存。堆内存越大,垃圾回收就会越频繁。这个视频对这个主题有很好的解释。

此外,在不使用时,请回收位图。堆上的垃圾回收是通过标记和扫描完成的,所以当您回收位图时它将释放内存。这样您的堆大小就不会增长并耗尽内存。

 bitmap.recycle();

加载位图的有效方法文档。查看在内存中加载缩小版本的方法。

除此之外,你可以使用Universal Image Loader。https://github.com/nostra13/Android-Universal-Image-Loader

https://github.com/thest1/LazyList。图片懒加载。

它们都使用缓存。


使用 bitmap.recycle() 如果没有使用实际上会有帮助。 - Rohit
使用了bitmap.recycle来处理未使用的位图。现在该模块不再崩溃了。谢谢。 - Rohit

5
您正在尝试访问比您拥有的更多的内存。请尝试使用:
 BitmapFactory.Options opts=new BitmapFactory.Options();
        opts.inDither=false;                    
        opts.inSampleSize = 8;                   
        opts.inPurgeable=true;                 
        opts.inInputShareable=true;             
        opts.inTempStorage=new byte[16 * 1024]; 

Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.h1)
        , 65,65, true),

同时,请查看以下链接以增加内存:

http://developer.android.com/reference/android/R.styleable.html#AndroidManifestApplication_largeHeap

在Android中检测应用程序堆大小

编辑1

尝试使用Nostra的图像下载器,您可以使用它来显示本地存储中的图像。 它还可以很好地管理内存...

https://github.com/nostra13/Android-Universal-Image-Loader


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接