Frage

Ich habe eine Listenansicht mit ein paar Tasten Bild auf jeder Zeile. Wenn Sie die Listenzeile klicken, startet sie eine neue Tätigkeit. Ich habe mit der Kamera Layout aufgrund eines Problems meiner eigenen Registerkarten zu bauen. Die Aktivität, die für das Ergebnis gestartet wird ist eine Karte. Wenn ich auf meiner Schaltfläche klicken die Bildvorschau (laden Sie ein Bild von der SD-Karte) die Anwendung kehrt von der Aktivität zurück zur listview Aktivität zum Ergebnis Handler starten, um meine neue Tätigkeit wiederzubeleben, die nichts anderes als ein Bild-Widget ist.

Die Bildvorschau auf der Listenansicht wird mit dem Cursor und ListAdapter getan. Dies macht es ziemlich einfach, aber ich bin nicht sicher, wie ich ein Bild mit geänderter Größe (Ie Kleinere Bitgröße nicht Pixel als src für die Grafik-Taste auf der Fliege setzen. Also habe ich nur das Bild der Größe verändert, die die Handy-Kamera kamen aus.

Das Problem ist, dass ich nicht genügend Arbeitsspeicher Fehlermeldung erhalten, wenn es versucht, zurück zu gehen und wieder in Gang bringt die 2. Aktivität.

  • Gibt es eine Möglichkeit ich die Liste Adapter bauen kann durch Zeile leicht rudern, wo ich im Fluge kann die Größe ( bitweise )?

Dies wäre vorzuziehen, da muss ich auch auf die Eigenschaften der Widgets / Elemente in jeder Zeile einige Änderungen vornehmen, da ich nicht in der Lage bin eine Zeile mit dem Touchscreen auswählen, da der Fokus Problem. ( kann ich Rollkugel verwendet werden. )

  • Ich weiß, ich kann ein aus Band tut der Größe und mein Bildes speichern, aber das ist nicht wirklich das, was ich tun will, aber einiger Beispielcode für das wäre schön.

Sobald ich das Bild auf der Listenansicht deaktiviert es funktionierte gut wieder.

Zur Info: Das ist, wie ich es tat:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

Wo R.id.imagefilename ist ein ButtonImage.

Hier ist mein LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

Ich habe auch einen neuen Fehler, wenn ein Bild angezeigt wird:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
War es hilfreich?

Lösung

Andere Tipps

die OutOfMemory Fehler zu beheben, sollten Sie etwas tun:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

Diese inSampleSize Option reduziert den Speicherverbrauch.

Hier ist eine komplette Methode. Zuerst liest die Bildgröße ohne Dekodieren der Inhalt selbst. Dann ist es den besten inSampleSize Wert findet, soll es eine Potenz von 2 sein, und schließlich wird das Bild decodiert werden.

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    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_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            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;
}

Ich habe eine kleine Verbesserung zu Fedor Code. Es tut im Grunde das gleiche, aber ohne die (meiner Meinung nach) hässlich while-Schleife und es ergibt sich immer in einer Zweierpotenz. Ein großes Lob an Fedor für die ursprüngliche Lösung zu machen, war ich fest, bis ich seine gefunden, und dann diese konnte ich macht ein:)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

Ich komme aus iOS Erfahrung und ich war frustriert ein Problem mit etwas so Grundlegendes wie Laden und zeigt ein Bild zu entdecken. Immerhin jeder, dass dieses Problem ist, die versucht, eine vernünftige Größe Bilder anzuzeigen. Wie auch immer, hier sind die beiden Änderungen, die mein Problem behoben (und machten meinen app sehr reaktions).

1) Jedes Mal, wenn Sie BitmapFactory.decodeXYZ() tun, stellen Sie sicher, dass in einem BitmapFactory.Options mit inPurgeable zu true gesetzt passieren (und vorzugsweise mit inInputShareable auch true gesetzt).

2) NIE Bitmap.createBitmap(width, height, Config.ARGB_8888) verwenden. Ich meine NIE! Ich habe noch nie das Ding nicht erhöht Speicherfehler nach wenigen Durchgängen hat. Kein Betrag von recycle(), System.gc(), was auch immer geholfen. Es hob immer Ausnahme. Die eine andere Art und Weise, die tatsächlich funktioniert, ist ein Dummy-Bild in Ihrem Drawables zu haben (oder ein anderes Bitmap, die Sie 1 oben mit Schritt decodiert), neu skalieren, dass, was auch immer Sie wollen, dann manipulieren, um die resultierende Bitmap (wie es vorbei an einer Leinwand auf für mehr Spaß). Also, was sollten Sie stattdessen: Bitmap.createScaledBitmap(srcBitmap, width, height, false). Wenn Sie aus irgendeinem Grund die Brute-Force-create-Methode verwenden müssen, dann zumindest passieren Config.ARGB_4444.

Dies ist fast garantiert Ihnen Stunden, wenn nicht Tage zu speichern. All das Gerede über das Bild Skalierung etc. funktioniert nicht wirklich (es sei denn, Sie erwägen, falsche Größe oder verschlechtertes Bild eine Lösung).

Es ist ein bekannter Fehler , es ist nicht wegen der großen Dateien. Da Android die Drawables Caches, wird es nach der Verwendung von wenigen Bildern aus dem Speicher gehen. Aber ich habe einen alternativen Weg für sie gefunden, durch das Android-Standard-Cache-System zu überspringen.

Lösung : Verschieben Sie die Bilder auf „Assets“ Ordner und verwenden Sie die folgende Funktion BitmapDrawable zu erhalten:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}

Ich hatte das gleiche Problem und löste es durch die BitmapFactory.decodeStream oder decodeFile Funktionen zu vermeiden und stattdessen verwendet BitmapFactory.decodeFileDescriptor

decodeFileDescriptor sieht aus wie es verschiedene native Methoden als die decodeStream / decodeFile nennt.

Wie auch immer, was funktioniert hat war (man beachte, dass ich einige Optionen hinzugefügt, wie einige oben hatte, aber das ist nicht das, was den Unterschied gemacht. Entscheidend ist der Aufruf von BitmapFactory.decodeFileDescriptor statt decodeStream oder decodeFile ):

private void showImage(String path)   {

    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.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
    bfOptions.inTempStorage=new byte[32 * 1024]; 

    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;                        
}

Ich denke, es ein Problem mit der nativen Funktion in decodeStream / decodeFile verwendet wird. Ich habe bestätigt, dass eine andere native Methode aufgerufen wird, wenn decodeFileDescriptor verwenden. Auch das, was ich gelesen habe, ist „dass Bilder (Bitmaps) sind nicht in einer Standard-Java Art und Weise zugeordnet, sondern über native Anrufe, die Zuweisungen außerhalb des virtuellen Heap getan, aber sind gezählt dagegen! "

Ich denke, am besten Weg, um die OutOfMemoryError zu vermeiden, ist es zu Gesicht und zu verstehen.

Ich habe eine App absichtlich OutOfMemoryError zu verursachen und Speichernutzung überwachen .

Nachdem ich eine Menge Experimente mit dieser App getan habe, habe ich die folgenden Schlussfolgerungen bekommen:

Ich werde reden über SDK-Versionen vor Honey Comb zuerst.

  1. Bitmap wird in nativen Heap gespeichert, aber es wird Müll automatisch gesammelt bekommen, ruft Recycling () ist überflüssig.

  2. Wenn {VM-Heap-Größe} + {zugeordnet nativen Heap-Speicher}> = {VM Heap-Größenbegrenzung für das Gerät}, und Sie versuchen, Bitmap zu erstellen, werden OOM geworfen werden.

    . HINWEIS: VM Heapgröße gezählt wird und nicht VM allokierten Speicher

  3. VM Heap Größe wird nie nach gewachsen schrumpfen, auch wenn der zugewiesenen VM-Speicher geschrumpfte wird.

  4. So ermitteln Sie die Spitzen VM Speicher halten müssen so gering wie möglich zu groß VM Heap-Größe zu halten, wächst für Bitmaps verfügbare Speicher zu speichern.

  5. manuell aufrufen System.gc () bedeutungslos ist, wird das System zunächst anrufen, bevor Sie die Heap-Größe wachsen.

  6. Native Heap-Größe wird nie schrumpfen, aber es ist nicht für OOM, so dass keine Notwendigkeit zu kümmern gezählt.

Dann lassen Sie uns darüber reden, SDK Starts von Honey Comb.

  1. Bitmap in VM-Heap gespeichert ist, india Speicher ist für OOM nicht gezählt.

  2. Die Bedingung für OOM ist viel einfacher: {VM-Heap-Größe}> = {VM Heap-Größenbegrenzung für das Gerät}.

  3. Sie mehr verfügbare Speicher erstellen Bitmap mit der gleichen Heap-Größenbeschränkung, OOM ist es weniger wahrscheinlich geworfen werden.

Hier finden Sie einige meiner Beobachtungen über Garbage Collection und Speicherverlust.

Sie können es sehen, sich in der App. Wenn eine Aktivität einen AsyncTask ausgeführt, die noch ausgeführt wurden, nachdem die Aktivität zerstört wurde, wird die Aktivität nicht Müll, bis die AsyncTask Finish gesammelt erhalten.

Das ist, weil AsyncTask eine Instanz einer anonymen inneren Klasse ist, ist es eine Referenz der Aktivität hält.

Beim AsyncTask.cancel (true) wird nicht die Ausführung stoppen, wenn die Aufgabe in einem IO-Betrieb im Hintergrund-Thread blockiert wird.

Rückrufe sind anonyme innere Klassen zu, so dass, wenn eine statische Instanz in Ihrem Projekt sie hält und befreit sie nicht, würden Speicher verloren werden.

Wenn Sie geplant eine sich wiederholende oder Aufgabe verzögert, beispielsweise ein Timer, und Sie rufen Sie nicht abbrechen () und Löschen () in onPause () würde Speicher verloren werden.

Ich habe eine Menge Fragen über OOM Ausnahmen und Caching in letzter Zeit gesehen. Der Entwickler Guide hat einen wirklich guten Artikel auf diesem, aber einige neigt dazu, auf deren Umsetzung in geeigneter Weise zum scheitern verurteilt.

Aus diesem Grunde schrieb ich eine Beispiel-Anwendung, das Caching in einer Android-Umgebung zeigt. Diese Implementierung hat noch keine OOM bekommen.

Schauen Sie am Ende dieser Antwort für einen Link zum Quellcode.

Anforderungen:

  • Android API 2.1 oder höher (ich konnte einfach nicht den verfügbaren Speicher für eine Anwendung in API 1.6 bekommen verwalten - das ist das einzige Stück Code, der nicht in API funktioniert 1.6)
  • Android Support-Paket

Screenshot

Features:

  • Behält den Cache, wenn es eine Orientierungsänderung ist , mit einem Singleton
  • Mit ein Achtel der zugeordneten Anwendungsspeicher in den Cache (ändern, wenn Sie möchten)
  • Große Bitmaps wird skaliert (Sie können die maximalen Pixel definieren, die Sie zulassen mögen)
  • Regler , dass es eine Internetverbindung zur Verfügung , bevor die Bitmaps Download
  • Stellt sicher, dass Sie nur instanziieren eine Aufgabe pro Zeile
  • Wenn Sie schleudert die ListView weg, es wird einfach nicht laden Sie die Bitmaps zwischen

Dies beinhaltet nicht:

  • Disk-Caching. Dies sollte auf jeden Fall einfach zu implementieren sein - nur auf eine andere Aufgabe hinweist, dass die Bitmaps von der Platte
  • packt

Beispielcode:

Die Bilder, die heruntergeladen werden, sind Bilder (75x75) von Flickr. setzen jedoch unabhängig von Bild-URLs Sie verarbeitet werden soll, und die Anwendung wird es verkleinern, wenn sie den maximalen Wert überschreitet. Bei dieser Anwendung sind die URLs einfach in einem String Array.

Die LruCache hat eine gute Art und Weise mit Bitmaps zu beschäftigen. Doch in dieser Anwendung habe ich eine Instanz eines LruCache in einer anderen Cache-Klasse, die ich geschaffen, um die Anwendung mehr möglich zu erhalten.

Cache.java kritische Sachen (die loadBitmap() Methode ist das wichtigste):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

Sie sollten nicht brauchen etwas in der Cache.java Datei zu bearbeiten, wenn Sie Disk-Caching implementieren möchten.

MainActivity.java kritische Sachen:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

getView() wird sehr häufig genannt. Es ist normalerweise keine gute Idee, Bilder dort zum Download, wenn wir nicht einen Scheck umgesetzt haben, die uns sicher, dass wir nicht eine unendliche Menge an Fäden pro Zeile beginnen. Cache.java prüft, ob der rowObject.mBitmapUrl bereits in einer Aufgabe ist und wenn es ist, wird es nicht eine andere starten. Daher werden wir höchstwahrscheinlich nicht die Arbeit Warteschlange Beschränkung des AsyncTask Pool übersteigt.

Download:

Sie können den Quellcode herunterladen von https://www.dropbox.com/s /pvr9zyl811tfeem/ListViewImageCache.zip .


Letzte Worte:

ich dies für ein paar Wochen getestet habe, habe ich eine einzige OOM Ausnahme noch nicht bekommen. Ich habe dies auf dem Emulator getestet, auf meinem Nexus One und auf meinem Nexus S. I getestet Bild Urls haben, die Bilder enthalten, die in HD-Qualität. Der einzige Engpass ist, dass es mehr Zeit zum Download nimmt.

Es ist nur ein mögliches Szenario, in dem ich mir vorstellen kann, dass die OOM erscheinen wird, und das ist, wenn wir viele, wirklich große Bilder herunterladen, und bevor sie skaliert erhalten und in den Cache setzen, wird ein gleichzeitig mehr Speicher benötigen und verursachen OOM. Aber das ist nicht evsowieso en eine ideale Situation und es wahrscheinlich nicht möglich sein, in einem gangbaren Weg zu lösen.

Fehler berichten in den Kommentaren! : -)

Ich habe folgendes um das Bild zu nehmen und es im laufenden Betrieb zu ändern. Hoffe, das hilft

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    

Es scheint, dass dies ein sehr langen Lauf Problem, mit vielen unterschiedlichen Erklärungen. Ich nahm den Rat der beiden häufigsten hier präsentierten Antworten, aber keiner von ihnen meine Probleme der VM gelöst behauptete, es nicht die Bytes leisten konnte, die Decodierung ein Teil des Prozesses auszuführen. Nach einigem Graben erfuhr ich, dass das eigentliche Problem hier ist der Decodierungsprozess weg von den NATIVE Haufen nehmen.

Sehen Sie hier: BitmapFactory OOM mich verrückt

Das mich in einer anderen Diskussion führen, wo ich noch ein paar Lösungen für dieses Problem gefunden. Eine davon ist manuell callSystem.gc(); nach dem Bild angezeigt wird. Aber das macht eigentlich Ihre Anwendung mehr Speicher, in dem Bemühen, den nativen Haufen zu reduzieren. Die bessere Lösung als die Freisetzung von 2.0 (Donut) ist die BitmapFactory Option „inPurgeable“ zu verwenden. Also habe ich einfach hinzugefügt o2.inPurgeable=true; kurz nach o2.inSampleSize=scale;.

Mehr zu diesem Thema hier: ist die Grenze der Speicherhaufen nur 6 M?

Nun, all dies gesagt hat, ich bin ein absoluter Dummkopf mit Java und Android auch. Also, wenn Sie denken, das eine schreckliche Art und Weise ist es, dieses Problem zu lösen, sind Sie wahrscheinlich Recht. ;-) Aber das hat Wunder für mich gearbeitet, und ich habe es unmöglich gefunden nun die VM aus Heap-Cache laufen. Der einzige Nachteil ich finden kann, ist, dass Sie Ihre zwischengespeichert gezeichnete Bild sind Wegwerfen. Was bedeutet, wenn Sie zurück zu diesem Bild gehen, sind neu gezeichnet Sie es jedes Mal. Im Fall von, wie meine Anwendung funktioniert, das ist nicht wirklich ein Problem. Ihre Ergebnisse können variieren.

leider , wenn keine der oben genannten Arbeiten, dann Fügen Sie diese auf Ihre Manifest Datei. Innerhalb Anwendung Tag

 <application
         android:largeHeap="true"

Mit diesem bitmap.recycle(); Dies trägt dazu bei, ohne die Bildqualität Problem.

Ich habe eine viel effektivere Lösung, die nicht Skalierung jeglicher Art benötigt. nur einmal dekodieren einfach Ihre Bitmap und dann in einer Karte gegen seinen Namen zwischenzuspeichern. Dann holen Sie einfach die Bitmap gegen den Namen und legen Sie es in der Image. Es gibt nichts, was getan werden muss.

Das funktioniert, weil die tatsächlichen binären Daten des decodierten Bitmap nicht in dem Dalvik VM-Heap gespeichert werden. Es wird extern gespeichert. Also jedes Mal, wenn Sie ein Bitmap-dekodieren, ordnet es Speicher außerhalb von VM-Heap, die niemals durch GC freigegeben wird

Damit Sie besser dies zu schätzen wissen, stellen Sie sich ur Bild im ziehbar Ordner gehalten haben. Sie erhalten nur das Bild von einer GetResources tun (). GetDrwable (R.drawable.). Dies wird nicht das Bild jedes Mal dekodieren aber wiederverwenden eine bereits dekodiert Instanz jedes Mal wenn Sie es nennen. Also im Grunde ist es im Cache gespeichert.

Nun, da Ihr Bild in einer Datei irgendwo (oder sogar von einem externen Server kommen), es ist Ihre Verantwortung, die dekodiert Bitmap-Instanz zwischenzuspeichern jeden wiederverwendet werden, wo es gebraucht wird.

Hope, das hilft.

Ich habe das gleiche Problem auf folgende Weise gelöst werden.

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);

Keine der Antworten oben für mich gearbeitet, aber ich habe mit einer schrecklich hässlich Abhilfe kommen, die das Problem gelöst. Ich habe ein sehr kleines, 1x1 Pixel-Bild zu meinem Projekt als Ressource und lud sie in mein Image, bevor sie in der Garbage Collection aufrufe. Ich denke, es könnte sein, dass das Image nicht wurde die Bitmap der Freigabe, so GC nahm es nie auf. Es ist hässlich, aber es scheint jetzt zu funktionieren.

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.

Große Antworten hier, aber ich wollte .. ein voll nutzbar Klasse , um dieses Problem zu lösen, so habe ich ein.

Hier ist meine BitmapHelper Klasse das ist OutOfMemoryError Beweis: -)

import java.io.File;
import java.io.FileInputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class BitmapHelper
{

    //decodes image and scales it to reduce memory consumption
    public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
    {
        try
        {
            //Decode image size
            BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
            bitmapSizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

            // load image using inSampleSize adapted to required image size
            BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
            bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
            bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
            bitmapDecodeOptions.inPurgeable = true;
            bitmapDecodeOptions.inDither = !quickAndDirty;
            bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

            Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

            // scale bitmap to mathc required size (and keep aspect ratio)

            float srcWidth = (float) bitmapDecodeOptions.outWidth;
            float srcHeight = (float) bitmapDecodeOptions.outHeight;

            float dstWidth = (float) requiredWidth;
            float dstHeight = (float) requiredHeight;

            float srcAspectRatio = srcWidth / srcHeight;
            float dstAspectRatio = dstWidth / dstHeight;

            // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
            // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
            // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
            // I do not excatly understand why, but this way it's OK

            boolean recycleDecodedBitmap = false;

            Bitmap scaledBitmap = decodedBitmap;
            if (srcAspectRatio < dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                // will recycle recycleDecodedBitmap
                recycleDecodedBitmap = true;
            }
            else if (srcAspectRatio > dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                recycleDecodedBitmap = true;
            }

            // crop image to match required image size

            int scaledBitmapWidth = scaledBitmap.getWidth();
            int scaledBitmapHeight = scaledBitmap.getHeight();

            Bitmap croppedBitmap = scaledBitmap;

            if (scaledBitmapWidth > requiredWidth)
            {
                int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }
            else if (scaledBitmapHeight > requiredHeight)
            {
                int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }

            if (recycleDecodedBitmap)
            {
                decodedBitmap.recycle();
            }
            decodedBitmap = null;

            scaledBitmap = null;
            return croppedBitmap;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
     * 
     * @param requiredWidth
     * @param requiredHeight
     * @param powerOf2
     *            weither we want a power of 2 sclae or not
     * @return
     */
    public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
    {
        int inSampleSize = 1;

        // Raw height and width of image
        final int srcHeight = options.outHeight;
        final int srcWidth = options.outWidth;

        if (powerOf2)
        {
            //Find the correct scale value. It should be the power of 2.

            int tmpWidth = srcWidth, tmpHeight = srcHeight;
            while (true)
            {
                if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                    break;
                tmpWidth /= 2;
                tmpHeight /= 2;
                inSampleSize *= 2;
            }
        }
        else
        {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
            final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    public static Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

}

Das funktioniert für mich.

Bitmap myBitmap;

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.InPurgeable = true;
options.OutHeight = 50;
options.OutWidth = 50;
options.InSampleSize = 4;

File imgFile = new File(filepath);
myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);

und das ist auf C # monodroid. Sie können ganz einfach den Pfad des Bildes ändern. Wichtig ist hier die Optionen festgelegt werden.

Dies scheint der geeignete Ort, um meine Utility-Klasse für die Be- und Verarbeitung von Bildern mit der Community zu teilen, Sie sind willkommen, es zu benutzen und ändern Sie es frei.

package com.emil;

import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * A class to load and process images of various sizes from input streams and file paths.
 * 
 * @author Emil http://stackoverflow.com/users/220710/emil
 *
 */
public class ImageProcessing {

    public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    public static Dimensions getDimensions(InputStream stream) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Dimensions getDimensions(String imgPath) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    private static boolean checkDecode(BitmapFactory.Options options){
        // Did decode work?
        if( options.outWidth<0 || options.outHeight<0 ){
            return false;
        }else{
            return true;
        }
    }

    /**
     * Creates a Bitmap that is of the minimum dimensions necessary
     * @param bm
     * @param min
     * @return
     */
    public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
        int newWidth, newHeight;
        switch(min.type){
        case WIDTH:
            if(bm.getWidth()>min.minWidth){
                newWidth=min.minWidth;
                newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case HEIGHT:
            if(bm.getHeight()>min.minHeight){
                newHeight=min.minHeight;
                newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case BOTH: // minimize to the maximum dimension
        case MAX:
            if(bm.getHeight()>bm.getWidth()){
                // Height needs to minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
                if(bm.getHeight()>min.minDim){
                    newHeight=min.minDim;
                    newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }else{
                // Width needs to be minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
                if(bm.getWidth()>min.minDim){
                    newWidth=min.minDim;
                    newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }
            break;
        default:
            // No resize
            newWidth=bm.getWidth();
            newHeight=bm.getHeight();
        }
        return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
    }

    public static int getScaledWidth(int height, Bitmap bm){
        return (int)(((double)bm.getWidth()/bm.getHeight())*height);
    }

    public static int getScaledHeight(int width, Bitmap bm){
        return (int)(((double)bm.getHeight()/bm.getWidth())*width);
    }

    /**
     * Get the proper sample size to meet minimization restraints
     * @param dim
     * @param min
     * @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
     * @return
     */
    public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
        switch(min.type){
        case WIDTH:
            return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
        case HEIGHT:
            return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
        case BOTH:
            int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
            int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
            // Return the smaller of the two
            if(widthMaxSampleSize<heightMaxSampleSize){
                return widthMaxSampleSize;
            }else{
                return heightMaxSampleSize;
            }
        case MAX:
            // Find the larger dimension and go bases on that
            if(dim.width>dim.height){
                return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
            }else{
                return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
            }
        }
        return 1;
    }

    public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
        int add=multipleOf2 ? 2 : 1;
        int size=0;
        while(min<(dim/(size+add))){
            size+=add;
        }
        size = size==0 ? 1 : size;
        return size;        
    }

    public static class Dimensions {
        int width;
        int height;

        public Dimensions(int width, int height) {
            super();
            this.width = width;
            this.height = height;
        }

        @Override
        public String toString() {
            return width+" x "+height;
        }
    }

    public static class Minimize {
        public enum Type {
            WIDTH,HEIGHT,BOTH,MAX
        }
        Integer minWidth;
        Integer minHeight;
        Integer minDim;
        Type type;

        public Minimize(int min, Type type) {
            super();
            this.type = type;
            switch(type){
            case WIDTH:
                this.minWidth=min;
                break;
            case HEIGHT:
                this.minHeight=min;
                break;
            case BOTH:
                this.minWidth=min;
                this.minHeight=min;
                break;
            case MAX:
                this.minDim=min;
                break;
            }
        }

        public Minimize(int minWidth, int minHeight) {
            super();
            this.type=Type.BOTH;
            this.minWidth = minWidth;
            this.minHeight = minHeight;
        }

    }

    /**
     * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
     * @param width
     * @param height
     * @param config
     * @return
     */
    public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
        long pixels=width*height;
        switch(config){
        case ALPHA_8: // 1 byte per pixel
            return pixels;
        case ARGB_4444: // 2 bytes per pixel, but depreciated
            return pixels*2;
        case ARGB_8888: // 4 bytes per pixel
            return pixels*4;
        case RGB_565: // 2 bytes per pixel
            return pixels*2;
        default:
            return pixels;
        }
    }

    private static BitmapFactory.Options getOptionsForDimensions(){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        return options;
    }

    private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = sampleSize;
        options.inScaled = false;
        options.inPreferredConfig = bitmapConfig;
        return options;
    }
}

In einem meiner Anwendung muss ich Bild machen entweder von Camera/Gallery. Wenn der Benutzer auf das Bild klicken von der Kamera (kann 2MP, 5MP oder 8MP sein), variiert die Bildgröße von kBs zu MBs. Wenn die Bildgröße kleiner (oder bis zu 1-2MB) über Code funktioniert gut, aber wenn ich Bild der Größe über 4 MB oder 5 MB dann kommt OOM in Rahmen: (

Dann habe ich gearbeitet, um dieses Problem zu lösen und schließlich habe ich die unten Verbesserung Fedor (alle Kredit zu Fedor zur Herstellung einer solchen schöne Lösung) hergestellt Code:)

private Bitmap decodeFile(String fPath) {
    // Decode image size
    BitmapFactory.Options opts = new BitmapFactory.Options();
    /*
     * If set to true, the decoder will return null (no bitmap), but the
     * out... fields will still be set, allowing the caller to query the
     * bitmap without having to allocate the memory for its pixels.
     */
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
                                // memory, the Bitmap can be cleared
    opts.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

    BitmapFactory.decodeFile(fPath, opts);

    // The new size we want to scale to
    final int REQUIRED_SIZE = 70;

    // Find the correct scale value. 
    int scale = 1;

    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) opts.outHeight
                / (float) REQUIRED_SIZE);
        final int widthRatio = Math.round((float) opts.outWidth
                / (float) REQUIRED_SIZE);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }

    // Decode bitmap with inSampleSize set
    opts.inJustDecodeBounds = false;

    opts.inSampleSize = scale;

    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(
            Bitmap.Config.RGB_565, false);

    return bm;

}

Ich hoffe, dies wird die Freunden hilft das gleiche Problem!

für mehr siehe das

Ich lief in dieser Frage vor ein paar Minuten. Ich löste es, indem bei der Verwaltung meines Listview-Adapters, einen besseren Job zu tun. Ich dachte, es war ein Problem mit den Hunderten von 50x50px Bilder, die ich benutzte, stellte sich heraus, ich habe versucht, meine Gewohnheit aufzublasen jedes Mal sehen die Zeile angezeigt wurde. Einfach durch, wenn die Zeile zu sehen Testen hatte aufgeblasen ich diesen Fehler beseitigt, und ich Hunderte von Bitmaps verwenden. Dies ist eigentlich für einen Spinner, aber die Basis-Adapter arbeiten alle gleich für eine Listview. Diese einfache Lösung verbessert auch erheblich die Leistung des Adapters.

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    if(convertView == null){
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.spinner_row, null);
    }
...

Ich habe den ganzen Tag damit verbracht, diese Lösungen und das einzige, was zu testen, die für mich gearbeitet ist die oben genannten Ansätze für das Bild bekommen und manuell die GC Aufruf, die ich kenne, ist nicht notwendig sein sollte, aber es ist die einzige Sache, die funktioniert, wenn ich meine app unter schweren Lasttests zwischen den Aktivitäten Schalt setzen. Meine App hat eine Liste von Miniaturbildern in einer Listenansicht in (lässt Aktivität A sagen), und wenn Sie auf einem dieser Bilder klicken sie Sie auf eine andere Tätigkeit nimmt (lässt Aktivität B sagen), die für das Element eines Hauptbildes zeigt. Wenn ich würde hin und her zwischen den beiden Tätigkeiten wechseln, würde ich schließlich die OOM-Fehler bekommen und die App würde schließen erzwingen.

Wenn ich auf halbem Weg die Listview bekommen würde, es würde abstürzen.

Wenn ich jetzt die B folgende Aktivitäts implementieren, ich durch die gesamte Listenansicht ohne Problem gehen und halten zu gehen und gehen und gehen ... und sein viel schneller.

@Override
public void onDestroy()
{   
    Cleanup();
    super.onDestroy();
}

private void Cleanup()
{    
    bitmap.recycle();
    System.gc();
    Runtime.getRuntime().gc();  
}

Dieses Problem tritt nur in Android Emulatoren. Ich sah sich auch dieses Problem in einem Emulator, aber wenn ich in einem Gerät überprüft dann es hat gut funktioniert.

So überprüfen Sie bitte in einem Gerät. Es kann in der Vorrichtung ausgeführt werden.

Meine 2 Cent: Ich löste meine OOM Fehler mit Bitmaps durch:

a) Skalierung meiner Bilder um einen Faktor von 2

b) unter Verwendung von Picasso Bibliothek in meinem benutzerdefinierten Adapter für eine Listview, mit einem Ein-Aufruf in getView wie folgen aus: Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);

verwendet diesen Code für jedes Bild in wählen Sie aus dem SD-Karte oder drewable Bitmap-Objekt zu konvertieren.

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

Verwenden des Bildpfad instend von ImageData_Path.get (img_pos) .getPath () .

Alle Lösungen hier erfordern eine IMAGE_MAX_SIZE Einstellung. Dies begrenzt Geräte mit leistungsfähigerer Hardware und wenn die Bildgröße zu gering ist, sieht es hässlich auf dem HD-Bildschirm.

Ich kam mit einer Lösung aus, die mit meinem Samsung Galaxy S3 und einige andere Geräte, die weniger Mächtigen arbeitet, mit einer besseren Bildqualität, wenn ein leistungsfähigeres Gerät verwendet wird.

Der Kern ist es die maximale Speicher für die Anwendung auf einem bestimmten Gerät zugeordnet zu berechnen und dann den Maßstab möglich zu sein, ohne diese niedrigsten Speicher überschreitet. Hier ist der Code:

public static Bitmap decodeFile(File f)
{
    Bitmap b = null;
    try
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        try
        {
            BitmapFactory.decodeStream(fis, null, o);
        }
        finally
        {
            fis.close();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        try
        {
            b = BitmapFactory.decodeStream(fis, null, o2);
        }
        finally
        {
            fis.close();
        }
    }
    catch (IOException e)
    {
    }
    return b;
}

ich die maximale Speicher dieses Bitmap auf 25% der maximalen zugewiesenen Speicher zu sein, müssen Sie diese an Ihre Bedürfnisse anpassen und sicherstellen, dass diese Bitmap wird gereinigt und bleiben nicht in den Speicher, wenn Sie fertig sind es benutzen. Normalerweise verwende ich diesen Code Bildrotation (Quelle und Ziel Bitmap) so mein App benötigt laden 2 Bitmaps im Speicher zur gleichen Zeit auszuführen, und 25% gibt mir einen guten Puffer ohne aus dem Speicher ausgeführt wird, wenn die Bilddrehung durchführt.

Hope, das hilft jemand da draußen ..

Eine solche OutofMemoryException nicht vollständig gelöst werden, indem die System.gc() Aufruf und so weiter.

Unter Bezugnahme auf das Aktivität Life Cycle

Die Aktivitätszustände durch das Betriebssystem selbst unterliegt die Speichernutzung für jeden Prozess und die Priorität eines jeden Prozesses bestimmt werden.

Sie können die Größe und die Auflösung für jeden der Bitmap-Bilder verwendet betrachten. Ich empfehle die Größe zu reduzieren, um geringere Auflösung sampeln, die Gestaltung von Galerien beziehen (ein kleinen Bild PNG, und ein Originalbild.)

Im allgemeinen Android-Gerät Heap-Größe nur 16 MB ist (variiert von Gerät / O siehe Post Heap Größen ), wenn Sie die Bilder geladen und es kreuzt die Größe von 16 MB, ist es aus der Erinnerung Ausnahme werfen, anstatt die Bitmap der Verwendung für, Bilder von der SD-Karte oder von Ressourcen geladen oder sogar von Netzwerk versuchen mit getImageUri , Laden Bitmap mehr Speicher benötigen, oder Sie können Bitmap auf null gesetzt, wenn Ihre Arbeit mit diesem Bitmap getan.

Dieser Code wird dazu beitragen, große Bitmap aus ziehbar laden

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top