web-dev-qa-db-ja.com

Androidで大きな画像をロードし、メモリ不足エラーを回避する方法は?

私は大きな画像(1390×870:150kb-50kb)を使用するアプリを開発しています。トリガー/ ImageViewをタップすると画像を追加しています。

特定の時点で、メモリ不足エラーが発生しています:

Java.lang.OutOfMemoryError
E/AndroidRuntime(23369): at Android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime(23369): at Android.graphics.BitmapFactory.decodeStream(BitmapFactory.Java:613)
E/AndroidRuntime(23369): at Android.graphics.BitmapFactory.decodeFile(BitmapFactory.Java:378)

画像のサイズを変更するには、これを実行しています:

Bitmap productIndex = null;
final String imageLoc = IMAGE_LOCATION;
InputStream imageStream;
try {
     imageStream = new FileInputStream(imageLoc);
     productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);

     productIV.setImageBitmap(productIndex);
     } catch (FileNotFoundException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
     }
}


public static Bitmap decodeSampledBitmapFromResource(Resources res, String resId, int reqWidth, int reqHeight) {

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(resId, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(resId, options);
}

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

    final int halfHeight = height / 3;
    final int halfWidth = width / 3;

    // Calculate the largest inSampleSize value that is a power of 2 and keeps both
    // height and width larger than the requested height and width.
    while ((halfHeight / inSampleSize) > reqHeight
            && (halfWidth / inSampleSize) > reqWidth) {
        inSampleSize *= 2;
    }
}

return inSampleSize;
}

Android Docs: 大きなビットマップを効率的にロードする

ログによると、これはdecodeSampledBitmapFromResourceメソッドの犯人です:

return BitmapFactory.decodeFile(resId, options);

-----編集-----各アイテムをFrameLayoutに追加する方法は次のとおりです。

for(int ps=0;ps<productSplit.size();ps++){
    //split each product by the equals sign
    List<String> productItem = Arrays.asList(productSplit.get(ps).split("="));

    String tempCarID = productItem.get(0);
    tempCarID = tempCarID.replace(" ", "");
    if(String.valueOf(carID).equals(tempCarID)){

        ImageView productIV = new ImageView(Configurator.this);
        LayoutParams productParams = new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        productIV.setId(Integer.parseInt(partIdsList.get(x)));
        productIV.setLayoutParams(productParams);

        final String imageLoc = productItem.get(2);

        InputStream imageStream;
        try {
            imageStream = new FileInputStream(imageLoc);
            productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);
            productIV.setImageBitmap(productIndex);
        } catch (FileNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        productLayers.addView(productIV);

    }
}
16
dcp3450

別のビットマップ構成を使用して、画像のサイズを大幅に減らすことができます。デフォルトはRGB-config ARGB8888で、4つの8ビットチャネルが使用されることを意味します(赤、緑、青、alhpa)。アルファはビットマップの透明度です。これは多くのメモリを占有します-画像サイズX4。したがって、画像サイズが4メガピクセルの場合、16メガバイトがすぐにヒープに割り当てられ、すぐにメモリを使い果たします。

代わりに-RGB_565を使用します。これにより品質がある程度低下しますが、これを補正するために画像をディザリングできます。

そのため、メソッドdecodeSampledBitmapFromResourceに次のスニペットを追加します。

 options.inPreferredConfig = Config.RGB_565;
 options.inDither = true;

あなたのコードで:

 public static Bitmap decodeSampledBitmapFromResource(Resources res, String resId, int    reqWidth, int reqHeight) {

 // First decode with inJustDecodeBounds=true to check dimensions
 final BitmapFactory.Options options = new BitmapFactory.Options();
 options.inJustDecodeBounds = true;
 BitmapFactory.decodeFile(resId, options);

 // Calculate inSampleSize
 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

 // Decode bitmap with inSampleSize set
 options.inJustDecodeBounds = false;
 options.inPreferredConfig = Config.RGB_565;
 options.inDither = true;
 return BitmapFactory.decodeFile(resId, options);
 }

参照:

http://developer.Android.com/reference/Android/graphics/Bitmap.Config.html#ARGB_8888

29

drawable-xxhdpiである適切なフォルダーに画像がない場合、S4などの高解像度デバイスは通常、メモリを使い果たします。画像をdrawable-nodpiに入れることもできます。 Android=が画像が低解像度用に設計されていると考えて画像を拡大縮小するdrawableにある場合、メモリ不足になります。

25
serge

この美しいライブラリを使用できます https://github.com/davemorrissey/subsampling-scale-image-view

4
Rohit Arya

Here is how I'm adding each item to the FrameLayoutそれが問題です。コードはさらに画像を追加および追加し続けます。サイズの変更やデバイスのメモリ量は問題ではなく、ある時点でメモリが不足します。これは、追加するすべての画像がメモリに保持されるためです。

この種の状況では、アプリはビューをリサイクルできるViewGroupを使用します。レイアウトはわかりませんが、通常はListViewGridView、またはViewPagerです。ビューをリサイクルすることにより、レイアウトを再利用し、画像を再ロードして破棄できます必要に応じて。

画像の読み込みとサイズ変更の特定の目的のために、非常によく書かれていて、使いやすく、安定しているPicassoライブラリを使用することを強くお勧めします。

2
Budius

画面のサイズの3倍以上の合計スペースを割り当てようとはしないので、ビットマップメモリ​​を管理する必要があります(スクロール動作に意味がある場合)。 1つの画像を別の画像の上に重ねている場合、ある時点で、メモリ不足エラーが発生しています。使用可能なメモリ内に収まるように、前の画面イメージを単一の背景イメージとしてキャプチャする必要がある場合があります。または、新しい画像が既存の画像と重なる場合、表示部分のみをロードしてレンダリングします。パフォーマンスが問題になる場合は、OpenGLテクスチャを検討する必要があるかもしれませんが、メモリ割り当ての問題は同じです。

ビットマップトレーニングの表示 をすべて実行してください。表示を処理する方法についての追加のアイデアが得られるはずです。

0
Morrison Chang

Frescoライブラリを使用して大きな画像をロードすると、このエラーが回避されます。 xmlレイアウトで

<com.facebook.drawee.view.SimpleDraweeView
    Android:id="@+id/my_image_view"
    Android:layout_width="1300dp"
    Android:layout_height="1300dp"
    fresco:placeholderImage="@drawable/my_drawable"
  />

およびjavacodeで

Uri uri = Uri.parse("https://image.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);
0
lee