AsyncTask verursacht OutOfMemory Exception

S

samarek

Fortgeschrittenes Mitglied
2
Hi,

ich hab folgendes Problem und zwar ...

führe ich beim start meiner App einen AsyncTask aus, der Daten aus einer fast 6mb großen JSON-Datei liest und die in eine Datenbank schreibt.
Wobei die App dann nach nicht allzulanger Zeit eine absehbare "OutOfMemoryException" auslöst.

hier ist mal ein vergleichbarer Quellcodeteil (der echte ist noch einiges länger)
Code:
InputStream is = context.getResources().openRawResource(availibilityJson);
        BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        StringBuilder alles = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null)
        {
            alles.append(line);
        }
        is.close();
        jsonArray = new JSONArray(alles.toString());
        for (int i = 0; i < jsonArray.length(); i++)
        {
            JSONObject obj = jsonArray.getJSONObject(i);
            String ean = obj.getString("EAN").trim();
            String vk6 = obj.getString("vk6").trim();
            Availability availability = new Availability(ean, vk6);
            Datenbank.availibilityEintragen(availability);
        }
Meine Frage ist jetzt, wie kann ich diesen Code ein wenig "glätten" damit er nicht mit so großen Datenmengen hantieren muss?
Also quasi 100 Datensätze lesen und in die Datenbank schreiben, dann wieder alles aus dem Speicher schmeissen und die nächsten 100 Datensätze verarbeiten.
 
JSONObject zu nutzen bei so großen Datenmengen ist vllt nicht das richtige. Evtl. auf ein SAX Parser umstellen. und du solltest nicht unnütz "Availability" Objekte erzeugen um dise dann zu übergeben, kannst die Daten auch direkt in die DB eintragen. Was du noch probieren könntest, die Objekte wenn du sie nicht mehr brauchst auf NULL zu setzen und hoffe das der GC ordentlich aufräumt.

Desweiteren solltest du ordentliche Variablen Namen verwenden und keine Mischung zwischen Deutsch und Englisch machen oO

Gruß
Michael
 
Hi samarek,

was sagt den der logcat genau dazu? Also mich interessieren die GC_ Einträge zwischen dem Start und dem OFME.

Hier gibt auch was zu lesen Designing for Performance | Android Developers
Mir hatte es etwas geholfen.

Wenn du dazu noch was nützliches erfährst, würde es mich auch interessieren. Auf solche Probleme stoße ich in letzter Zeit öfters :(

regards
 
Soo hab es jetzt hinbekommen, schien echt daran zuliegen dass er soviele Objekte erzeugen musste.

Jetzt laufen alle 4 "Sachen aus JSON in die Datenbank schreiben" Tasks vernünftig durch.

Allerdings hab ich jetzt das Problem dass das eintragen der 6mb Datei über 10 Minuten dauert, das läuft natürlich im Prinzip nur bei der ersten Initialisierung durch, aber ist dennoch viel zu lang.
Weiss da vielleicht jemand Möglichkeiten wie sich das einlesen von JSON weiter optimieren lässt, also mindestens um die Hälfte müsste ich die Zeit schon noch drücken die das "in die Datenbank schreiben" im Moment braucht.
 
Ich weiß nicht, ob man bei Android und SQLite Prepared Statements und Transaktionen benutzen kann (hab es bis jetzt nicht benötigt).

Aber im normalen Java mit JDBC hab ich bei Datenimporten durch Prepared Statements und Transaktionen schon öfter 50% Zeit gespart. Wäre sicherlich ein Versuch wert ;-)
 
Ja, Android unterstützt Transaktionen und Prepared Statements

Ich hab jetzt mit Hilfe von preparedStatements und Transaktionen das Ausführen aller 4 Tasks von ca. 15 Minuten auf 22 Sekunden (!!!) reduziert.
Mehrfach getestet alle Daten sind da, Freunde sagen dass ist durchaus im Bereich des Möglichen, aber ich kann es nicht ganz glauben.

Was sagt ihr dazu?
 
Na wenn du es mehrfach getestet hast und alle Daten da sind, dann wirds schon stimmen :D

Kannst mal den Teil mit den Statements posten? Würd mich mal interessieren, wie der Code dazu aussieht.
 
Hier mal die Kurzversion einer der Methoden, geht sicher noch schöner, aber bin erstmal froh dass es läuft

Code:
public static boolean availabilityEintragen(String ean, String vk6, int i, boolean letzter)
    {
        if ((i % 1000) == 0)
        {
            db.beginTransaction();
        }
        try
        {
            String sql = "INSERT INTO availability (EAN, vk6) VALUES (?, ?)"; 
            SQLiteStatement stmt = db.compileStatement(sql);
            stmt.bindString(1, ean);
            stmt.bindString(2, vk6);
            stmt.execute();
            if ((i % 1000) == 999 || letzter)
            {
                db.setTransactionSuccessful();
            }
            return true;
        }
        catch (Exception exc)
        {
            Log.v("Exception", exc.getMessage());
            return false;
        }
        finally
        {
            if ((i % 1000) == 999 || letzter)
            {
                db.endTransaction();
            }
        }
    }
1000 Inserts werden zu einer Transaktion zusammengefasst und beim letzten Datensatz wird die laufende Transaktion beendet
 
  • Danke
Reaktionen: DieGoldeneMitte
hmm, ich denke
Code:
            String sql = "INSERT INTO availability (EAN, vk6) VALUES (?, ?)"; 
            SQLiteStatement stmt = db.compileStatement(sql);
gehört nicht in die Schleife, weil du ja jedes mal das Statement neu erstellst. Glaub in die Schleife kommt nur noch das bind und das execute rein, oder?
 
Die Methode wird in einer Schleife aufgerufen, in der Methode selber gibt es keine Schleife.
Wäre es denn soviel sinnvoller das Statement und den String ausserhalb zu erzeugen und dann jedesmal an die Methode zu übergeben?
 
Deine Methode ist statisch, dann kannste doch auch die Variablen statisch machen. Fragst sie anfangs auf NULL ab, wenn NULL dann initialisieren, ansonsten nur noch bind, execute usw.

Der Sinn von einem Prepared Statement sollte doch sein, daß man es nur 1 mal initialisiert und nicht in jedem Durchlauf. Zumindest hab ich das so immer verstanden ;-)
 
Stimmt eigentlich, wobei mein primäres Ziel ja jetzt die Reduzierung der Laufzeit war und das habe ich so ja erreicht

... aber vielleicht krieg ich es ja noch von 22 Sekunden auf 20 Sekunden gedrückt ;)
 

Ähnliche Themen

M
Antworten
8
Aufrufe
1.678
swa00
swa00
L
  • LHomme
Antworten
1
Aufrufe
1.155
jogimuc
J
Zurück
Oben Unten