私はアンドロイドアプリケーションに取り組んでいます。アプリケーションには、多くの画像を含むビューがあります。エラーが発生しました。誰かが私にいくつかの提案をしてくれることを期待して、できるだけ多くの情報を提供しようとします.
アプリケーションは、すべてのローカル テストでうまく機能していました。ただし、ユーザーから多くのクラッシュを受け取りました。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の使用を許可しない場合があります. また、私の最大の問題は、問題を再現できなかったことです。そのため、修正を試みるときは、ユーザーがまだエラーを受けているかどうかを確認するために少し待つ必要があります.
メモリ不足の問題を回避するために、さらに何を試みることができますか? どんな提案でも大歓迎です。どうもありがとう