it-swarm.com.de

Wie lade ich große Bilder in Android und vermeide den Speicherfehler?

Ich arbeite an einer App, die große Bilder verwendet (1390 × 870: 150kb - 50kb). Ich füge Bilder hinzu, wenn ich auf einen Auslöser/ImageView tippe.

An einem bestimmten Punkt erhalte ich einen Speicherfehler:

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)

Um das Bild zu verkleinern, mache ich folgendes:

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;
}

Ich habe diese Art der Größenänderung, um Platz zu sparen, von den Android-Dokumenten erhalten: Große Bitmaps effizient laden

Dem Protokoll zufolge ist dies der Täter in der decodeSampledBitmapFromResource-Methode:

return BitmapFactory.decodeFile(resId, options);

----- edit -----Hier füge ich jedes Element zum FrameLayout hinzu.

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);

    }
}
15
dcp3450

Sie können eine andere Bitmap-Konfiguration verwenden, um die Größe der Bilder stark zu verringern. Die Standardeinstellung ist RGB-config ARGB8888, dh es werden vier 8-Bit-Kanäle verwendet (rot, grün, blau, alhpa). Alpha ist die Transparenz der Bitmap. Dies belegt viel Speicher - Bildergröße X 4. Wenn also die Bildgröße 4 Megapixel beträgt, werden 16 Megabytes sofort auf dem Heapspeicher zugewiesen - und der Speicher wird schnell aufgebraucht. 

Verwenden Sie stattdessen RGB_565, wodurch sich die Qualität in gewissem Maße verschlechtert. Um dies zu kompensieren, können Sie die Bilder jedoch verzerren.

Fügen Sie also zu Ihrer Methode decodeSampledBitmapFromResource die folgenden Ausschnitte hinzu:

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

In Ihrem Code:

 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);
 }

Verweise:

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

27

Hochauflösende Geräte wie S4 verfügen normalerweise nicht mehr über genügend Speicher, wenn sich das Bild nicht im richtigen Ordner (drawable-xxhdpi) befindet. Sie können Ihr Bild auch in drawable-nodpi ablegen. Der Grund dafür, dass es aus dem Speicher gehen würde, wenn Ihr Bild nur in drawable wäre, dass Android das Bild skalieren würde, da es der Ansicht ist, dass das Bild für eine niedrige Auflösung entworfen wurde.

25
serge

Sie können diese schöne Bibliothek verwenden https://github.com/davemorrissey/subsampling-scale-image-view

4
Rohit Arya

Here is how I'm adding each item to the FrameLayout Das ist das Problem. Der Code fügt ständig weitere Bilder hinzu, und es spielt keine Rolle, wie gut Sie die Größe ändern oder wie viel Speicherplatz das Gerät hat. Das liegt daran, dass jedes Bild, das Sie hinzufügen, im Gedächtnis bleibt.

Für diese Art von Situation verwenden die Apps eine ViewGroup, die Ansichten wiederaufbereiten kann. Ich kenne Ihr Layout nicht, aber normalerweise ist es eine ListView, GridView oder eine ViewPager. Durch das Wiederherstellen von Ansichten verwenden Sie das Layout erneut und können nach Bedarf Bilder erneut laden.

Zum Laden und Ändern der Größe von Bildern empfehle ich dringend, die Picasso-Bibliothek zu verwenden, da sie sehr gut geschrieben, einfach zu bedienen und stabil ist.

1
Budius

Sie müssen immer noch den Bitmap-Speicher verwalten, da ich nicht versuchen würde, den gesamten Speicherplatz mehr als das Dreifache der Bildschirmgröße zuzuweisen (wenn Sie darüber nachdenken, was für das Bildlaufverhalten sinnvoll ist). Wenn Sie ein Bild auf einem anderen Bild überlagern, tritt zu einem bestimmten Zeitpunkt der Fehler "Out Of Memory" auf. Möglicherweise müssen Sie die Aufnahme des vorherigen Bildschirmbilds als einzelnes Hintergrundbild betrachten, um sicherzustellen, dass Sie noch in den verfügbaren Speicher passen. Oder wenn ein neues Bild nur ein vorhandenes Bild überlappt, wird der sichtbare Teil geladen und gerendert. Wenn die Leistung zum Problem wird, müssen Sie möglicherweise OpenGL Textures in Betracht ziehen, das Speicherzuordnungsproblem ist jedoch immer noch dasselbe.

Gehen Sie das gesamte Display Bitmaps Training durch , da es einige zusätzliche Ideen für den Umgang mit der Anzeige geben sollte.

0
Morrison Chang

Verwenden Sie die Fresco-Bibliothek zum Laden großer Bilder, um diesen Fehler zu vermeiden Im XML-Layout

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

und in Javacode

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