web-dev-qa-db-ja.com

BitmapFactory.Options.inBitmapを使用したビットマップ割り当ては、IllegalArgumentExceptionをスローします

次の例外が発生します: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 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を通過して最初の画像を表示しますが、後でBitmapFactory.decodeResource() at_// #2_の画像をクリックするとクラッシュします)

いくつかのメモ:

  • 画像は同じサイズです。 jpgpngを試しました。両方で失敗します。
  • ビットマップは変更可能です。
  • このcanUseForInBitmapメソッドを使用して、 ここで説明 としてチェックしました。

質問:

この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);
_

次に、これは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を超えています!

inBitmapオプションなしでdecodeResource()を実行した場合も、同じことが起こります。それで、質問はまだ開いています、このプロパティをどのように使用するのですか?

21
sromku

エミュレートされたNexus4でコードを試しました。
デフォルトのic_launcher.pngファイルがあり、これをコピーしてdrawable-mdpiに2回貼り付けました(通常どおり)。コード内の名前と一致するように2つの新しいファイルの名前を変更しました(変更を少なくするため)。
アプリケーションを実行すると、あなたと同じように観察されました。
数回の試行の後、新しいpngを他の描画可能なフォルダーにコピーすることにしました。そのため、次の場所に存在していました。

  • ドローアブル-hdpi
  • drawable-mdpi
  • ドローアブル-xhdpi
  • ドローアブル-xxhdpi

アプリケーションを実行しましたが、機能しています。

なぜそれが実際に機能するのかはよくわかりませんが、明らかに正しい画面解像度/密度のものでなければなりません。

3
helleye

新しいビットマップを作成してから再利用しようとするのは少し奇妙です。そもそもdecodeResourceに新しいビットマップを作成させてみませんか? #2の問題は、ビットマップを使用するようにImageViewを設定すると、それを再利用できなくなることだと思います(すでに使用されているため)。 IllegalArgumentExceptiondocs に記載されています:

デコード操作でこのビットマップを使用できない場合、デコードメソッドはnullを返し、IllegalArgumentExceptionをスローします。

回避策としては、2つのビットマップを保持し、ImageViewが指しているビットマップを切り替えてみてください。例:

  1. ビットマップ1にデコードし、ImageViewをビットマップ1にポイントします
  2. ビットマップ2にデコードし、ImageViewをビットマップ2にポイントします
  3. 手順1から繰り返します
2
kabuko

コードの#1と#2の前に、options.inMutableをtrueに設定する必要があります。これは少し追加に聞こえるかもしれませんが、これらはあなたが踏んだ小さな地雷です。

お役に立てれば

1
Parth Kapoor

なぜビットマップをデコードしているのですか?ただしてください:

@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の前は、追加の制約が適用されます。デコードされる画像(リソースとしてまたはストリームとして)は、jpegまたはpng形式である必要があります。 inSampleSizeを1に設定して、同じサイズのビットマップのみがサポートされます。さらに、再利用されたビットマップの構成は、設定されている場合、inPreferredConfigの設定を上書きします。


編集:

リソースビットマップが大きい場合は、asynctaskにロードするだけです...詳細 [〜#〜] here [〜#〜] ただし、はるかに簡単に実行できます。

0
Gaskoin

#2でSamsungS3で発生した問題についてはよくわかりません。しかし、Nexus 4で発生した問題は、2つの画像を間違ったdpiドローアブルフォルダに置いたことが原因である可能性があります。そのため、ビットマップをデコードしようとすると、リソースが見つかりません。

私の携帯電話の画面密度はhdpiです。最初は、2つの画像をdrawable-mdpiに入れようとしましたが、#1で問題が発生しました。それで私はそれをdrawable-hdpiに変更しました、そしてそれは働きました。

0
Nam