捕获“RuntimeException: Canvas: trying to draw too large...”

11

我有一个应用程序,它会从文件系统中绘制图像到屏幕上,就像这样:

Bitmap image = BitmapFactory.decodeFile(file.getPath());
imageView.setImageBitmap(image);

如果图片很大,我会看到这个错误:
java.lang.RuntimeException: Canvas: trying to draw too large(213828900bytes) bitmap.
    at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:260)
    at android.graphics.Canvas.drawBitmap(Canvas.java:1415)
    ...

堆栈错误并没有到达我的代码。我该如何捕获此错误?或者有更适合的方法将图像绘制到imageView,以避免此错误发生?

你应该检查图片的大小,并加载较小尺寸的图片以避免异常。请阅读此文章:高效加载大型位图 你也可以使用 Picasso 库 - Matt Twig
3个回答

13

如果任何应用程序允许用户选择图片作为背景或在某处绘制,那么它必定会遇到一个问题,即如果用户选择了全景照片或大尺寸的图片,你的应用程序将强制关闭,并且你无法防止这种错误的发生,因为你无法捕获异常。

好的,我只需要捕获异常并告诉用户无法加载该图片,然后完成操作,使用Picasso或其他库来实现此额外功能太过冗余,Android编码已经够繁琐的了,我不想让它更加繁琐。

最后,我在core.java.android.view.DisplayListCanvas中找到了代码,其中定义了变量MAX_BITMAP_SIZE以限制图片的最大大小。

private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB

您的程序中无法读取该变量,因为它被定义为私有的(但如果您有任何方式可以读取该变量,请告诉我),以下是抛出RuntimeException的部分代码:

int bitmapSize = bitmap.getByteCount();
if (bitmapSize > MAX_BITMAP_SIZE) {
    throw new RuntimeException(
        "Canvas: trying to draw too large(" + bitmapSize + "bytes) bitmap.");
    }

我只是复制上面的部分,在我的代码中检查位图的大小,如果超过100M,则向用户显示我之前提到的消息,然后结束。

如果您的情况与我相同,希望这可以帮到您。


2
@KurtVandenBranden 这是一个答案,不是另一个问题,这可能是自从我两年前提出问题以来最有用的答案。 - Milk
你怎么获取MAX_BITMAP_SIZE的值?我似乎找不到它在哪里。 - MetaSnarf
这篇文章已经过时了,Android之前有很多愚蠢的问题,但现在它们已经修复了。 - Eyes Blue

4
位图太大,位图对象无法处理。因此,ImageView也应该有同样的问题。解决方案:在像paint.net这样的程序中调整图像大小,或为位图设置固定大小并进行缩放。
在进一步之前,您的堆栈跟踪链接到位图的绘制,而不是创建对象:
``` at android.graphics.Canvas.drawBitmap(Canvas.java:1415) ```
因此,您可以执行以下操作:
Bitmap image = BitmapFactory.decodeFile(file.getPath());//loading the large bitmap is fine. 
int w = image.getWidth();//get width
int h = image.getHeight();//get height
int aspRat = w / h;//get aspect ratio
int W = [handle width management here...];//do whatever you want with width. Fixed, screen size, anything
int H = W * aspRat;//set the height based on width and aspect ratio

Bitmap b = Bitmap.createScaledBitmap(image, W, H, false);//scale the bitmap
imageView.setImageBitmap(b);//set the image view
image = null;//save memory on the bitmap called 'image'

或者,如这里所提到的,您也可以使用Picasso

注意

您尝试加载的图像大小为213828900字节,即213mb。这可能是一个分辨率非常高的图像,因为它们越大,它们的字节越多。

对于这么大的图像,缩放方法可能不能正常工作,因为它会牺牲太多的质量。对于这么大的图像,Picasso或Glide可能是仅有的选项,可以在不损失太多分辨率的情况下加载图片。


1
它没有保持纵横比,我们不知道原始位图的大小。 - Matt Twig
编辑以反映 - Zoe stands with Ukraine
1
我在使用 Picasso 时仍然遇到这个问题。 - Dushyant Suthar
@DushyantSuthar 我没有使用过Picasso(或Glide)。我说它可能会有所帮助。你唯一能做的就是减小图像大小,因为Bitmap实现(或者,可能找到一个不关心图像大小的不同实现)。 - Zoe stands with Ukraine
我的图片是150*150的小图,大概在50-60Kb左右。但是我仍然遇到了这个错误。我已经尝试使用Glide和Picasso两种方式,但都没有成功:( - Rohit Singh

1
我修复了LunarWatcher代码中的错误。
Bitmap image = BitmapFactory.decodeFile(file.getPath());
float w = image.getWidth();//get width
float h = image.getHeight();//get height
int W = [handle width management here...];
int H = (int) ( (h*W)/w);
Bitmap b = Bitmap.createScaledBitmap(image, W, H, false);//scale the bitmap
imageView.setImageBitmap(b);//set the image view
image = null;//save memory on the bitmap called 'image'

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