位图分配,使用BitmapFactory.Options.inBitmap抛出IllegalArgumentException

我得到下一个exception:将inBitmap设置为true时,将问题解码到现有的位图 ;

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

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

  • API:4.4.2,Nexus 4
  • API:4.3.1,三星s3

这是我的代码,它是这个DevBytes:位图分配video中显示的副本。

 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在// #1 BitmapFactory.decodeResource()上崩溃
  • S3,传递#1并显示第一个图像,但稍后通过点击位于// #2 BitmapFactory.decodeResource()处的图像

几个注意事项:

  • 图像尺寸相同。 我试过jpgpng 。 两个都失败了。
  • 位图是可变的。
  • 我使用这个canUseForInBitmap方法来检查,如下所述 。

题:

如何正确使用这个inBitmap属性?

如果你遇到这样的问题,或者你看到我做了一些愚蠢的事情,请评论/回复。 任何帮助将不胜感激。 如果你知道任何解决方法,这将是伟大的。

– 编辑(问题仍然是打开的) –

对不起,没有解释为什么我试图以这种方式重用位图的原因。
原因是GC每次决定释放内存时都会locking。
inBitmapfunction应该帮助我们重新使用位图而不分配新的内存,这将导致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); 

那么这将是GC工作

 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 

这是超过100ms的locking主线程!

同样的事情会发生,如果我将decodeResource()没有inBitmap选项。 所以这个问题还是开放的,如何使用这个属性?

Solutions Collecting From Web of "位图分配,使用BitmapFactory.Options.inBitmap抛出IllegalArgumentException"

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

  • 绘制,华电国际
  • 绘制,MDPI
  • 绘制,xhdpi
  • 绘制,xxhdpi

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

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

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

如果解码操作不能使用这个位图,解码方法将返回null,并将抛出IllegalArgumentException。

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

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

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

希望这可以帮助

为什么你在解码位图? 做就是了:

 @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); } }); } 

你确定你的图片有相同的尺寸? 根据文件

如果设置了[inBitmap],则在加载内容时,采用Options对象的解码方法将尝试重新使用此位图。 如果解码操作不能使用这个位图,解码方法将返回null,并将抛出IllegalArgumentException。 当前的实现需要重用的位图是可变的,并且即使在解码通常会导致不可变的位图的资源的情况下,所得到的重用位图也将继续保持可变。

您仍然应该始终使用解码方法的返回位图,并且不要因为上面列出的约束和可能发生的故障情况而重复使用位图。 检查返回值是否与Options结构中设置的inBitmap的值相匹配,将指示位图是否被重用,但是在所有情况下,都应使用解码函数返回的位图来确保使用的位图解码目的地。

与BitmapFactory的用法

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

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


编辑:

如果你有大量的资源位图,只需要加载它asynctask …更多信息在这里,但它可以做得更简单。

我不确定你在三号二号遇到的问题。 但是,您遇到的Nexus 4问题可能是因为您将两个图像放在了错误的dpi可绘制文件夹中。 所以当它尝试解码位图时,找不到资源。

我的手机的屏幕密度是hdpi,起初我试图把两个图像放在drawable-mdpi中,我面对#1的问题。 所以我把它改成了drawable-hdpi,而且工作。

只需在应用程序标记(AndroidManifest.xml)中放入largeHeap =“true”