使用BitmapFactory.Options.inBitmap进行位图分配会抛出IllegalArgumentException

时间:2014-03-12 15:03:21

标签: android bitmap garbage-collection bitmapfactory

我将下一个例外:将问题解码为现有位图,将inBitmap设置为true;

  

引起:java.lang.IllegalArgumentException:解码到现有位图的问题
在android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:460)
  ...

有趣的是,运行时,相同的代码在不同的地方失败:

  • API:4.4.2,Nexus 4
  • API:4.3.1,Samsung s3

这是我的代码,它是此DevBytes: Bitmap Allocation视频中显示的副本。

private BitmapFactory.Options options;
private Bitmap reusedBitmap;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final ImageView imageView = (ImageView) findViewById(R.id.image_view);

    // set the size to option, the images we will load by using this option
    options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    options.inMutable = true;
    BitmapFactory.decodeResource(getResources(), R.drawable.img1, options);

    // we will create empty bitmap by using the option
    reusedBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.ARGB_8888);

    // set the option to allocate memory for the bitmap
    options.inJustDecodeBounds = false;
    options.inSampleSize = 1;
    options.inBitmap = reusedBitmap;

    // #1
    reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img1, options);
    imageView.setImageBitmap(reusedBitmap);

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            options.inBitmap = reusedBitmap;
            // #2
            reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img2, options);
            imageView.setImageBitmap(reusedBitmap);

        }
    });
}

  • Nexus 4,BitmapFactory.decodeResource() // #1
  • 时崩溃
  • S3,传递#1并显示第一张图片,但稍后点击{{1>}处的图片 BitmapFactory.decodeResource()
  • 会崩溃

很少有人注意到:

  • 图像尺寸相同。我尝试了// #2jpg。两者都失败了。
  • 位图是可变的。
  • 我使用此png方法检查了described here

问题:

如何正确使用此canUseForInBitmap属性?

如果您遇到此类问题或者您发现我做了些蠢事,请发表评论/回复。任何帮助将不胜感激。如果您了解任何解决方法,那就太棒了。

- 编辑(问题仍然是开放的) -

很抱歉没有解释为什么我试图以这种方式重用位图的原因。原因是每次他决定释放内存时, GC 会锁定。
inBitmap功能应该帮助我们重用位图而不分配新的内存,这会导致GC清理已经分配的内存。

例如,如果我使用这种常用方法:

inBitmap

然后这将是GC工作

Log.i("my_tag", "image 1");
imageView.setImageResource(R.drawable.img1);
Log.i("my_tag", "image 2");
imageView.setImageResource(R.drawable.img2);
Log.i("my_tag", "image 3");
imageView.setImageResource(R.drawable.img3);

锁定主线程超过 100ms

如果我I/my_tag ( 5886): image 1 D/dalvikvm( 5886): GC_FOR_ALLOC freed 91K, 2% free 9113K/9240K, paused 15ms, total 15ms I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.914MB for 11520016-byte allocation D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20362K/20492K, paused 13ms, total 13ms I/my_tag ( 5886): image 2 D/dalvikvm( 5886): GC_FOR_ALLOC freed 11252K, 2% free 9111K/9236K, paused 15ms, total 15ms I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.912MB for 11520016-byte allocation D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 35ms, total 35ms I/my_tag ( 5886): image 3 D/dalvikvm( 5886): GC_FOR_ALLOC freed 11250K, 2% free 9111K/9236K, paused 15ms, total 15ms I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.913MB for 11520016-byte allocation D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 32ms, total 32ms 没有decodeResource()选项,也会发生同样的事情。所以问题仍然存在,如何使用这个属性?

6 个答案:

答案 0 :(得分:3)

我尝试使用模拟的Nexus 4代码。
我有默认的ic_launcher.png文件,我在drawable-mdpi中复制并粘贴了两次(就像我通常那样)。我重命名了两个新文件以匹配代码中的名称(因此我做的更改较少) 当我运行应用程序时,我观察到的与你一样 经过几次不同的尝试,我决定将新的png复制到其他可绘制的文件夹 - 所以它们存在于:

  • 抽拉-HDPI
  • 抽拉-MDPI
  • drawable-xhdpi
  • 抽拉-xxhdpi

我运行应用程序,它正在运行!

我不确定为什么它真的有效,但显然它必须具有正确的屏幕分辨率/密度。

答案 1 :(得分:2)

创建一个新的位图然后尝试重用它有点奇怪。为什么不让decodeResource首先创建一个新的位图?我怀疑你在#2中的问题是,一旦你设置ImageView使用位图,它就不能再重复使用它了(因为它已经在使用中)。 docs中提到了IllegalArgumentException

  

如果解码操作无法使用此位图,则decode方法将返回null并将抛出IllegalArgumentException。

就解决方法而言,您可能会尝试保留两个位图并切换ImageView所指向的位图,例如:

  1. 解码为位图1,将ImageView指向位图1
  2. 解码到位图2,将ImageView指向位图2
  3. 从第1步开始重复

答案 2 :(得分:1)

在#1&amp;之前,您需要将options.inMutable设置为true。代码中的#2。 这可能听起来有点添加,但这些都是你所踩过的小地雷。

希望这有帮助

答案 3 :(得分:0)

为什么要解码位图?只是做:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final ImageView imageView = (ImageView) findViewById(R.id.image_view);
    imageView.setImageResource(R.drawable.img1);

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            imageView.setImageResource(R.drawable.img2);
        }
    });
}

你确定你的两个图像都有相同的尺寸吗?根据{{​​3}}

  

如果设置了[inBitmap],则解码采用Options对象的方法   尝试在加载内容时重用此位图。如果解码   操作不能使用这个位图,解码方法将返回null   并将抛出IllegalArgumentException。目前的实施   需要重用的位图是可变的,结果是必需的   即使在解码时,重用的位图仍将保持可变   通常会导致不可变位图的资源。

     

您仍应始终使用返回的解码方法Bitmap   并且由于约束,不假设重用位图有效   以上概述和可能发生的故障情况。检查是否   返回值与Options中设置的inBitmap的值匹配   结构将指示位图是否被重用,但在所有情况下都是你   应该使用解码函数返回的Bitmap来确保   您正在使用用作解码目标的位图。

     

使用BitmapFactory

     

从KITKAT开始,BitmapFactory可以重用任何可变位图   解码任何其他位图,只要得到的字节数   解码后的位图小于或等于分配的字节数   重用的位图。这可能是因为内在尺寸较小,   或其缩放后的尺寸(密度/样本大小)较小。

     

在KITKAT之前适用其他约束:正在解码的图像   (无论是作为资源还是作为流)必须采用jpeg或png格式。   仅支持相同大小的位图,inSampleSize设置为1。   此外,重用位图的配置将覆盖   设置inPreferredConfig,如果设置。


修改

如果您有大型资源位图,只需将其加载到asynctask ...更多信息documentation但它可以更加简单。

答案 4 :(得分:0)

我不确定您在#2遇到的问题。但是你遇到Nexus 4的问题可能是因为你把两个图像放在了错误的dpi drawable文件夹中。因此,当它尝试解码位图时,它无法找到资源。

我的手机屏幕密度是hdpi,起初我尝试将两个图像放在drawable-mdpi中,我面对的问题是#1。所以我将它改为drawable-hdpi,并且它有效。

答案 5 :(得分:-4)

只需输入largeHeap =&#34; true&#34;在应用程序标签(AndroidManifest.xml)