Problem mit OutOfMemory (VMHeap) und base64 in JSON

  • 8 Antworten
  • Letztes Antwortdatum
B

Boris0815

Fortgeschrittenes Mitglied
4
Hallo,

ich habe eine App in der ich von einem Server Daten in Form von JSON bekomme. In manchen JSON-Objekten sind BASE64-codierte Bilder. Wenn ich nun versuche die empfangenen Daten in JSON umzuwandeln kann es zu einer "out of memory"-Exception führen, da der heap zu klein ist.
E/dalvikvm-heap Out of memory on a 610302-byte allocation.
Dies passiert nur auf sehr wenigen Geräten, bzw. in einem Emulator mit klein konfiguriertem VMHeap. Aber ich möchte natürlich das es auf allen Geräten geht. :)
An dieser Stelle knallt es:

Code:
                if (statusCode == 200) {                
                    HttpEntity entity = response.getEntity();
                    InputStream content = entity.getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                    String line;

                    while ((line = reader.readLine()) != null) {
                      builder.append(line); // StringBuilder
                    }
                    reader.close();

                    JSONObject jsonObject = new JSONObject(builder.toString());
Wenn das empfange JSON zu groß ist, "verbraucht" der StringBuilder zu viel Speicher und die Exception kommt.

Ich habe nun schon einiges probiert und gelesen, bin aber noch nicht auf die richtige Lösung gestoßen.

Kennt ihr eine bessere Möglichkeit wie ich an mein JSON-Objekt komme, um auch leistungsschwächere Geräte zu unterstützen?
Die Daten die ich empfange kann ich erstmal nicht beeinflußen. Android 2.3.0 sollte mindest Version bleiben.

Ich dachte an etwas, wie die Strings zu stückeln und an anderer Stelle als im Speicher zusammen zu bauen.

Bin für jede Idee dankbar. :)
Gruß
Boris
 
Die erste Frage... warum Bilder als base64 in json? Normalerweise würde man dort die ID des Bildes reinpacken und dann das Bild (wenn man es dann braucht) per http abfragen.

Dein Code ist so für große Bilder extrem unperformant.

cu
 
Ja ich weiss, aber am Server kann ich erstmal nix machen.
Die Bilder sind eigentlich auch nicht so groß, aber halt groß genug das in seltenen Fällen der Fehler auftritt, wenn das Gerät etwas schwächer ist.
 
Was soll denn eigentlich die while Schleife? Du nimmst nen String, packst den in nen String Array und machst dann wieder nen String draus.

cu

Der ursprüngliche Beitrag von 20:18 Uhr wurde um 20:19 Uhr ergänzt:

Kannst du getContent() nicht direkt übergeben?

Der ursprüngliche Beitrag von 20:19 Uhr wurde um 20:22 Uhr ergänzt:

Nur so spontan... irgendwie scheint mir dein Ansatz... normalerweise sollte das einfacher gehen. json und http gehören doch eh zusammen.

cu
 
So direkt steckt da derString nicht drinn. Man kann noch die Methode aus dem "org.apache.http.util.EntityUtils" package nehmen, ändert in meinem Fall allerdings nicht viel.

String json = EntityUtils.toString(entity);
 
du könntest es mal mit jackson oder gson probieren, dann musst du nicht selbst den kompletten string einmal in den speicher lesen. Vielleicht machen die das schlauer.
siehe z.b. hier:

java - Load an Object using Gson - Stack Overflow

ansonsten sehe ich nicht viel chance, wenn das bild zu groß für den speicher ist.
 
  • Danke
Reaktionen: Boris0815
Gson kann es leider auch nicht. :(
Muss ich wohl mal versuchen meinen Server ändern zu lassen.
 
Hi,

kurze Frage: ein Bild pro Json oder mehrere?

Du solltest vielleicht die gesamte Internetkommunikation in einen Service auslagern (vielleicht in einen eigenen Prozess). Dann hast du den ganzen Wirbel mit dem UIThread nicht. Außerdem wäre es eine Option, die Bilder hintereinander zu laden.

Auch solltest du nicht HttpEntity benutzen. Seit fünf Jahren wird das package org.apache.http.* nicht mehr aktiv von Google unterstützt. Es ist langsamer und störanfälliger als UrlConnection. Und ist seit 5.1 endgültig decrepated.
 
  • Danke
Reaktionen: Boris0815
kurze Frage: ein Bild pro Json oder mehrere?

Es sind zwar mehrere, aber es kann schon bei einem auftreten.

Du solltest vielleicht die gesamte Internetkommunikation in einen Service auslagern (vielleicht in einen eigenen Prozess).

Wie meinst Du das?
Momentan setzte ich einen Call aus einer Activity ab. Der Call wird dann in einem asynchronen Task abgearbeitet und der evtl. Response per Observer an die Activity zurückgeben.

Auch solltest du nicht HttpEntity benutzen.
Danke für den Hinweis! Schau ich mir auf jeden Fall mal an. :thumbsup:
 
Zurück
Oben Unten