2016-06-28 13 views
3

Ich muss eine Menge Daten aus einer SQLite-Datenbank lesen und erstellen Sie eine ordnungsgemäß formatierte JSON.Android riesige JSONObject toString geht OutOfMemoryError

Ich erreiche dies zur Zeit durch ein Objekt namens RequestPayload, das einige ArrayList s enthält, in denen ich die Daten, die ich von SQLite gelesen habe.

Die RequestPayload Klasse hat eine parseJson() Methode, die eine JSONObject, auf dem zurückgibt ich die toString() Methode schließlich nennen meine JSON String zu erhalten, die schließlich auf eine Datei geschrieben wurde.

Jetzt, wenn ich "kleine" Datenmengen in SQLite habe, geht alles gut. Wenn ich eine Menge Daten haben, ist das, was passiert:

06-28 09:55:34.121 10857-6799/it.example.sampler E/AndroidRuntime: FATAL EXCEPTION: Thread-195240 
    Process: it.example.sampler, PID: 10857 
    Theme: themes:{com.cyanogenmod.trebuchet=overlay:system, com.android.settings=overlay:system, default=overlay:system, iconPack:system, fontPkg:system, com.android.systemui=overlay:system, com.android.systemui.navbar=overlay:system} 
    java.lang.OutOfMemoryError: Failed to allocate a 52962812 byte allocation with 16764752 free bytes and 41MB until OOM 
     at java.lang.StringFactory.newStringFromChars(Native Method) 
     at java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:629) 
     at java.lang.StringBuilder.toString(StringBuilder.java:663) 
     at org.json.JSONStringer.toString(JSONStringer.java:430) 
     at org.json.JSONObject.toString(JSONObject.java:690) 
     at it.example.sampler.controllers.network.RequestBodyEncoder.serialise(RequestBodyEncoder.java:69) 
     at it.example.sampler.controllers.network.RequestBodyEncoder.createPacket(RequestBodyEncoder.java:50) 
     at it.example.sampler.services.FileStoreRunnable.run(FileStoreRunnable.java:57) 
     at java.lang.Thread.run(Thread.java:818) 

06-28 09:55:34.509 10857-10857/it.example.sampler E/WindowManager: android.view.WindowLeaked: Activity it.example.sampler.ui.StartSamplingActivity has leaked window com.android.internal.policy.PhoneWindow$DecorView{74710d V.E...... R......D 0,0-1026,494} that was originally added here 
    at android.view.ViewRootImpl.<init>(ViewRootImpl.java:372) 
    at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299) 
    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:86) 
    at android.app.Dialog.show(Dialog.java:319) 
    at it.example.sampler.ui.StartSamplingActivity.executeStop(StartSamplingActivity.java:305) 
    at it.example.sampler.ui.StartSamplingActivity.onClickedButton(StartSamplingActivity.java:256) 
    at it.example.sampler.ui.StartSamplingActivity$3.onClick(StartSamplingActivity.java:190) 
    at android.view.View.performClick(View.java:5204) 
    at android.view.View$PerformClick.run(View.java:21158) 
    at android.os.Handler.handleCallback(Handler.java:739) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5461) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

der Code Jetzt:

die Fehlerzeile aus dieser Methode ist:

private String serialise() throws JSONException { 
    // Serialise 
    String sampleString = mPayload.parseJSON().toString(); // THIS LINE 

    Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString); 
    return sampleString; 
} 

und wenn bekam ich die LogCat korrekt ist der Fehler während der Ausführung der toString() Methode und es scheint, dass das JSON-Objekt zu groß ist.

Wie kann ich mit dieser Situation umgehen?

UPDATE: beantworten zu @YashJain Kommentar für die Größe des JSONObject zu fragen.

Nachdem eine Abtastung Abtastung dauerte 2 Stunden, die ich eine JSON hatte enthält:

  • JSONArray enthält 155,432 float s
  • JSONArray enthaltend 8.393 float s
  • JSONArray enthaltend 8.393 benutzerdefiniertes Objekt mit 5 float s jeder (und ein String)
  • ,432 int s

In Bezug auf die Bytes enthalten

  • 2D-Array, da ein Float von 4 Byte (Ich hoffe, dass ich das Kalkül richtig gemacht haben) Ich habe circa:

    (12 * 155432 * 4) + (2 * 8393 * 4) + (1 * 8393 * 5) + (1 * 155432 * 4) ~= 8.191.573 bytes 
    

    UPDATE: Ich wurde nach dem verwendeten Komprimierungsalgorithmus gefragt. Ich benutze ZLIB über die Java-Klassen Deflater und DeflaterOutputStream.

    So ist die Strömung:

    • Beispieldaten -> Speichern Sie sie in SQLite
    • Lesen von SQLite -> Build RequestPayload Objekt im Speicher
    • konvertieren RequestPayload Objekt JSONObject (mit benutzerdefinierten eine benutzerdefinierte Parser).
    • umrechnen JSONObject zu String (mit JSONObjecttoString() Methode)
    • das String-Bytes komprimiert (mit getBytes()) -> kodieren sie in Base64
    • Server

    EDIT final Base64 String senden: Um auf die "mögliche doppelte Flagge" zu antworten, habe ich diese Frage während meiner Recherchen übrigens nicht gesehen, die mir nicht geholfen hat, weil sie largeHeap Direktive benutzt (die ich nicht verwende). Auch gibt es keine akzeptierte Antwort und die einzige Antwort bietet keine praktische Lösung.

  • +2

    Mögliche Duplikate von [JSONObject.toString() gibt OutOfMemoryError] (http://stackoverflow.com/questions/32919833/jsonobject-tostring-returns-outofmemoryerror) – mdDroid

    +0

    Ich weiß nicht die Antwort, aber ich will wissen, wie große Daten es halten kann? Wie viele Bytes/Zeichen. –

    +0

    @mdDroid Ich habe eine Bearbeitung hinzugefügt, die erklärt, warum ich nicht die "possible duplicate" -Flag könnte helfen .. trotzdem danke für die Markierung, weil ich es nicht bei der Recherche vor der Frage zu sehen .. leider nicht Hilfe – FredMaggiowski

    Antwort

    1

    Entschuldigung dafür, dass ich keine weiteren Rückmeldungen gegeben habe Ich war mit anderen Arbeiten beschäftigt und dies wurde als letzter in meiner Prioritätswarteschlange.

    Egal, was tatsächlich für mich funktionierte, ist Google GSON Bibliothek.

    Meine Serialisierungsmethode hat einfach geworden:

    private String serialise() { 
        // Serialise 
        return mPayload.toStringFromGSON(); 
    } 
    

    Und Methode toStringFromGSON() in Klasse Payload ist:

    public String toStringFromGSON() { 
        GsonBuilder gsonBuilder = new GsonBuilder(); 
        Gson gson = gsonBuilder.create(); 
    
        return gson.toJson(this); 
    } 
    

    Wie auch immer ich denke, das nur eine temporäre Patch angesehen werden kann. Tatsächlich habe ich bemerkt, dass der von der Anwendung verwendete Speicher während dieser Phase dramatisch ansteigt. Daher denke ich, dass das Problem nur verschoben wird und bei höheren Lasten auftreten wird.

    0

    Ihr Qn ist sehr gut. Haben Sie versucht, indem Sie AsyncTask setzen, vielleicht funktioniert es durch Thread Handhabung.

    private String serialise() throws JSONException { 
        String sampleString = ""; 
    
        new AsyncTask<Void, Void, Void>() { 
         @Override 
         protected Void doInBackground(Void... voids) { 
          // Serialise 
          sampleString = mPayload.parseJSON().toString(); // THIS LINE. 
          return null; 
         } 
        }.execute(); 
    
        Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString); 
        return sampleString; 
    } 
    

    Ich nehme an, Sie haben gute Kenntnisse. Sorry, wenn es nicht funktioniert. Aber es war in meinem Fall. Überprüfen Sie bitte das.

    +0

    Hallo, danke für deine Antwort, Tatsächlich wird dieser Job in einer 'Runnable' Implementierung implementiert und läuft auf einem anderen' Thread' wie folgt: 'neuer Thread (neuer FileStoreRunnable (getApplicationContext(), mHandler)). start();' – FredMaggiowski

    0

    Sie werden das Problem bei großen Konvertierungen von Jsons haben, wenn Sie in einen String konvertieren, wird das System die gesamte Speichermenge einer eindeutigen Variablen zuweisen, und Sie werden folglich einen Speicherüberlauf haben.

    Der beste Weg, damit umzugehen, ist ein JSON-Streaming, das den JSON in Teilen lesen und analysieren wird.

    Ich denke, es hat keinen Sinn, das Rad neu zu erfinden, also empfehle ich eine Json-Parser-Bibliothek wie Gson oder Jackson zu verwenden, sie werden Ihren json automatisch in ein POJO transformieren und Sie können sie in einer SQLite-Datenbank speichern einige Datenbank-Assistenten wie eine ORMLite.

    Auf diese Weise können Sie einen riesigen JSON empfangen, ihn direkt zu einem POJO-Objekt analysieren und den POJO direkt in die Datenbank einfügen, wobei die Speicherverwaltung den Bibliotheken überlassen bleibt.

    In diesem site können Sie JSON analysieren und die gesamte Klasse in einem GSON- oder Jackson-Format abrufen, danach müssen Sie nur die ORMLite-Anmerkungen hinzufügen und den Database-Helper erstellen, es ist wirklich einfach.

    Verwandte Themen