私はAndroidアプリケーションに取り組んでいます。アプリケーションには多くの画像を含むビューがあります。エラーが発生しました。
アプリケーションは、すべてのローカルテストでうまく機能していました。しかし、ユーザーから多くのクラッシュを受け取りました:_Java.lang.OutOfMemoryError: bitmap size exceeds VM budget
_
これはスタックトレースです
_0 Java.lang.OutOfMemoryError: bitmap size exceeds VM budget
1 at Android.graphics.Bitmap.nativeCreate(Native Method)
2 at Android.graphics.Bitmap.createBitmap(Bitmap.Java:507)
3 at Android.graphics.Bitmap.createBitmap(Bitmap.Java:474)
4 at Android.graphics.Bitmap.createScaledBitmap(Bitmap.Java:379)
5 at Android.graphics.BitmapFactory.finishDecode(BitmapFactory.Java:498)
6 at Android.graphics.BitmapFactory.decodeStream(BitmapFactory.Java:473)
7 at Android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.Java:336)
8 at Android.graphics.BitmapFactory.decodeResource(BitmapFactory.Java:359)
9 at Android.graphics.BitmapFactory.decodeResource(BitmapFactory.Java:385)
_
私の最大の問題は、古いデバイスでも問題をローカルで再現できなかったことです。
これを解決するために多くのことを実装しました。
onDestroy()
メソッドに実装しましたinSampleSize
を計算します。正しいinSampleSizeを計算するコード
_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)
{
if(width > height)
{
inSampleSize = Math.round((float) height / (float) reqHeight);
}
else
{
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
_
ビットマップを取得するためのコード
_ // decodes image and scales it to reduce memory consumption
private static Bitmap decodeFile(File file, int newWidth, int newHeight)
{// target size
try
{
Bitmap bmp = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), Uri.fromFile(file));
if(bmp == null)
{
// avoid concurrence
// Decode image size
BitmapFactory.Options option = new BitmapFactory.Options();
// option = getBitmapOutput(file);
option.inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
option.inTargetDensity = res.getDisplayMetrics().densityDpi;
if(newHeight > 0 && newWidth > 0)
option.inSampleSize = calculateInSampleSize(option, newWidth, newWidth);
option.inJustDecodeBounds = false;
byte[] decodeBuffer = new byte[12 * 1024];
option.inTempStorage = decodeBuffer;
option.inPurgeable = true;
option.inInputShareable = true;
option.inScaled = true;
bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, option);
if(bmp == null)
{
return null;
}
}
else
{
int inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
int inTargetDensity = res.getDisplayMetrics().densityDpi;
if(inDensity != inTargetDensity)
{
int newBmpWidth = (bmp.getWidth() * inTargetDensity) / inDensity;
int newBmpHeight = (bmp.getHeight() * inTargetDensity) / inDensity;
bmp = Bitmap.createScaledBitmap(bmp, newBmpWidth, newBmpHeight, true);
}
}
return bmp;
}
catch(Exception e)
{
Log.e("Error calling Application.decodeFile Method params: " + Arrays.toString(new Object[]{file }), e);
}
return null;
}
_
古いデバイスのヒープサイズに基づいてイメージサイズを計算するコード
_private void calculateImagesSize()
{
// only for Android older than HoneyComb that does not support large heap
if(Build.VERSION.SDK_INT < Constants.HONEYCOMB)
{
long maxHeapSize = Runtime.getRuntime().maxMemory();
long maxImageHeap = maxHeapSize - 10485760;
if(Application.getResource().getDisplayMetrics().densityDpi >= DisplayMetrics.DENSITY_XHIGH)
{
maxImageHeap -= 12 * 1048576;
}
if(maxImageHeap < (30 * 1048576))
{
int screenHeight = Math.min(Application.getResource().getDisplayMetrics().heightPixels, Application.getResource()
.getDisplayMetrics().widthPixels);
long maxImageSize = maxImageHeap / 100;
long maxPixels = maxImageSize / 4;
long maxHeight = (long) Math.sqrt(maxPixels / 1.5);
if(maxHeight < screenHeight)
{
drawableHeight = (int) maxHeight;
drawableWidth = (int) (drawableHeight * 1.5);
}
}
}
}
_
問題はヒープにあると思います。OSがアプリケーションでmaxheapsizeを使用できない場合があります。また、私の最大の問題は、問題を再現できなかったことです。そのため、修正を試みるとき、ユーザーがまだエラーを受け取っているかどうかを確認するために少し待つ必要があります。
メモリ不足の問題を回避するために、さらに何ができますか?どんな提案も大歓迎です。どうもありがとう
この関数を使用してデコードするだけです...これはあなたのエラーに最適なソリューションです。同じエラーが発生し、このソリューションを得たためです..
public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
画像のサイズを縮小/拡大することで、メモリ不足の例外を取り除くことができます。これを試してください
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 6;
Bitmap receipt = BitmapFactory.decodeFile(photo.toString(),options); //From File You can customise on your needs.
こんにちは、ファイルをデコードする必要があります。そのためには、次の方法を試してください。
public static Bitmap new_decode(File f) {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
o.inDither = false; // Disable Dithering mode
o.inPurgeable = true; // Tell to gc that whether it needs free memory,
// the Bitmap can be cleared
o.inInputShareable = true; // Which kind of reference will be used to
// recover the Bitmap data after being
// clear, when it will be used in the future
try {
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 300;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 1.5 < REQUIRED_SIZE && height_tmp / 1.5 < REQUIRED_SIZE)
break;
width_tmp /= 1.5;
height_tmp /= 1.5;
scale *= 1.5;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
// o2.inSampleSize=scale;
o.inDither = false; // Disable Dithering mode
o.inPurgeable = true; // Tell to gc that whether it needs free memory,
// the Bitmap can be cleared
o.inInputShareable = true; // Which kind of reference will be used to
// recover the Bitmap data after being
// clear, when it will be used in the future
// return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
try {
// return BitmapFactory.decodeStream(new FileInputStream(f), null,
// null);
Bitmap bitmap= BitmapFactory.decodeStream(new FileInputStream(f), null, null);
System.out.println(" IW " + width_tmp);
System.out.println("IHH " + height_tmp);
int iW = width_tmp;
int iH = height_tmp;
return Bitmap.createScaledBitmap(bitmap, iW, iH, true);
} catch (OutOfMemoryError e) {
// TODO: handle exception
e.printStackTrace();
// clearCache();
// System.out.println("bitmap creating success");
System.gc();
return null;
// System.runFinalization();
// Runtime.getRuntime().gc();
// System.gc();
// decodeFile(f);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
実際問題は開発OSにあります。 Android iOSとは異なり、Googleの人々はこれをカメラの解像度に基づいて開発します。特に写真のようなリッチ画像の場合、ビットマップは多くのメモリを消費します。ここでAndroidそのピクセルに基づいて、キャプチャされた画像のみがメモリを使用します。したがって、明らかに高解像度の画像は低ピクセル容量の携帯電話ではアップロードされません。In Android osはすべてのアプリケーションに最大16MBを割り当てます。アップロードされた画像がこれを超える場合、Java.lang.OutofMemoryError:ビットマップサイズがVM予算が発生し、アプリケーションがクラッシュします。これを参照してください http://developer.Android.com/training/displaying-bitmaps/index.html
StackOverFlowの別の質問で提案の概要を書きました: Android:BitmapFactory.decodeStream()out of memory out of 400KB file with 2MB free heap
OOMを回避したい場合、OOMをキャッチして、イメージが解決できるまでsampleSizeを増やすことができます。
private Bitmap getBitmapSafely(Resources res, int id, int sampleSize) {
// res = context.getResources(), id = R.drawable.yourimageid
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize = sampleSize;
try {
bitmap = BitmapFactory.decodeResource(res,
id, options);
} catch (OutOfMemoryError oom) {
Log.w("ImageView", "OOM with sampleSize " + sampleSize, oom);
System.gc();
bitmap = getBitmapSafely(res, id, sampleSize + 1);
}
return bitmap;
}
それが役に立てば幸い。
エラーをキャッチするのは適切ではなく、単に回避策です。