Ich arbeite an einer Android-Anwendung. Die Anwendung hat eine Ansicht, die viele Bilder enthält. Ich hatte einen Fehler. Ich werde versuchen, so viele Informationen wie möglich zu geben, in der Hoffnung, dass mir jemand Anregungen geben kann.
Die Anwendung funktionierte bei allen lokalen Tests hervorragend. Ich habe jedoch viele Abstürze von Benutzern erhalten: Java.lang.OutOfMemoryError: bitmap size exceeds VM budget
Dies ist der Stack-Trace
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)
Mein größtes Problem ist, dass ich das Problem selbst auf alten Geräten nicht lokal reproduzieren konnte.
Ich habe viele Dinge implementiert, um das Problem zu lösen:
onDestroy()
-Methode implementiertinSampleSize
.Code zum Berechnen der korrekten 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;
}
Code zum Abrufen der Bitmap
// 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;
}
Code zum Berechnen der Bildgröße basierend auf der Heap-Größe für ältere Geräte
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);
}
}
}
}
Ich denke, das Problem liegt beim Heap, manchmal erlaubt das Betriebssystem der Anwendung nicht, maxheapsize zu verwenden. Mein größtes Problem ist auch, dass ich das Problem nicht reproduzieren konnte. Wenn ich also einen Fix versuche, muss ich ein wenig warten, um zu sehen, ob Benutzer den Fehler immer noch erhalten.
Was kann ich mehr versuchen, um Probleme mit dem Arbeitsspeicher zu vermeiden? Anregungen wären sehr dankbar. Danke vielmals
verwenden Sie diese Funktion einfach zum Dekodieren ... Dies ist die perfekte Lösung für Ihren Fehler. Weil ich auch den gleichen Fehler bekomme und diese Lösung erhalten habe.
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;
}
Durch Verkleinern/Skalieren der Größe des Bildes können Sie die Out of Memory-Ausnahme,
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 6;
Bitmap receipt = BitmapFactory.decodeFile(photo.toString(),options); //From File You can customise on your needs.
Hallo du musst die Datei entschlüsseln. Versuchen Sie dazu mit der folgenden Methode.
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;
}
}
eigentlich liegt das problem bei der entwicklung os. In Android wird dies im Gegensatz zu iOS von Google auf der Grundlage der Kameraauflösung entwickelt. Bitmaps beanspruchen viel Speicherplatz, insbesondere für umfangreiche Bilder wie Fotos. Verschiedene Kameras erfassen Bilder mit unterschiedlichen Pixeln (verschiedene Handys haben unterschiedliche Kamerapixelkapazitäten). Hier in Android, basierend auf diesen Pixeln, wird nur das aufgenommene Bild gespeichert. Daher wird ein Bild mit hoher Auflösung offensichtlich nicht von einem Telefon mit niedriger Pixelkapazität hochgeladen. In Android os werden jeder Anwendung maximal 16 MB zugewiesen. Wenn das hochgeladene Image länger dauert, wird Java.lang.OutofMemoryError: Die Bitmap-Größe überschreitet das Budget von VM und die Anwendung stürzt ab. Siehe hierzu http://developer.Android.com/training/displaying-bitmaps/index.html
Ich habe eine Zusammenfassung der Vorschläge in einer anderen StackOverFlow-Frage geschrieben: Android: BitmapFactory.decodeStream () hat nicht genügend Speicher mit einer 400-KB-Datei mit 2 MB freiem Heap
Wenn Sie OOM vermeiden möchten, können Sie OOM fangen und die Probengröße erhöhen, bis das Bild aufgelöst werden kann:
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;
}
Ich hoffe es hilft.
Es ist nicht geeignet, den Fehler abzufangen, nur eine Problemumgehung.