Sehr große SOAP-Antwort – Android – Fehler aufgrund unzureichenden Arbeitsspeichers

Lesezeit: 5 Minuten

Ich habe eine Anwendung, bei der ich eine große Datenmenge über einen SOAP-Aufruf an einen Webservice in die Anwendung herunterladen muss, wenn sie zum ersten Mal ausgeführt wird. Die Antwort wird dann an eine Funktion gesendet, die das XML konvertiert und die Daten in einer db-Datei speichert.

Die Daten sind mehr als 16 MB groß und ich habe jedes Mal einen java.lang.OutOfMemoryError.

Eine Änderung des Webservices zur Ausgabe kleinerer Datenmengen ist keine Option.

Gibt es eine Möglichkeit, große Datenmengen herunterzuladen? So etwas wie ein InputStream vielleicht?

Das ist mein Code

public Protocol[] getProtocols() {

    String METHOD_NAME = "GetProtocols";
    String SOAP_ACTION = "urn:protocolpedia#GetProtocols";
    Log.d("service", "getProtocols");
    SoapObject response = invokeMethod(METHOD_NAME, SOAP_ACTION);
    return retrieveProtocolsFromSoap(response);
}

private SoapObject invokeMethod(String methodName, String soapAction) {
    Log.d(TAG, "invokeMethod");
    SoapObject request = GetSoapObject(methodName);
    SoapSerializationEnvelope envelope = getEnvelope(request);
    return makeCall(envelope, methodName, soapAction);

}

Kann jemand vorschlagen, was in diesem Fall zu tun ist?

Danke und Grüße Mukul

  • Ich denke, vtd-xml kann zweifellos dazu beitragen, die Speichernutzung zu senken.

    – vtd-xml-Autor

    13. März 11 um 08:06 Uhr

Sehr grose SOAP Antwort – Android – Fehler aufgrund unzureichenden Arbeitsspeichers
Mukul Jain

Nur ein Update, ich habe festgestellt, dass der “call” -Methode in AndroidHttpTransport in dieser Zeile der Speicher ausgeht –

           if (debug) {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    byte[] buf = new byte[256];
                    while (true) {
                        int rd = is.read(buf, 0, 256);
                        if (rd == -1)
                            break;
                        bos.write(buf, 0, rd);
                    }
                    bos.flush();
                    buf = bos.toByteArray(); //Goes out of memory here
                    responseDump = new String(buf);
                    is.close();
                    is = new ByteArrayInputStream(buf);

Der Aufruf von toByteArray benötigt viel Speicher, um dies zu umgehen, schreibe ich die Antwort jetzt direkt in eine XML-Datei, anstatt sie in ein Byte-Array umzuwandeln, und diese wird an einem Ort meiner Wahl gespeichert. Hier –

if (debug) {
    FileOutputStream bos = new FileOutputStream("/data/data/com.mypackage.myapp/response.xml");
    byte[] buf = new byte[1048576];
    int current = 0; int i=0; int newCurrent = 0;
    while ((current = inputStream.read(buf)) != -1) {
        newCurrent = newCurrent + current;
    Log.d("current", "Current = " + current + " total = "+newCurrent+" i = "+i++);
                    bos.write(buf, 0, current);
                }
                bos.flush();
}

Dem Gerät geht nicht mehr der Arbeitsspeicher aus, und ich habe eine benutzerdefinierte Parsing-Methode, die dieses XML nimmt und in die DB schreibt.

  • Wie hast du das mit dem verwendet SoapObject in dem SoapSerializationEnvelope? Können Sie Ihr gesamtes Beispiel zeigen, indem Sie den Webdienst mit ksoap2 aufrufen und die Ergebnisse in eine Datei schreiben, anstatt sie in a SoapObject?

    – Sarah Gefäße

    6. Oktober 11 um 13:42 Uhr

  • Danke Mukul für deine Antwort. Es ist jedoch nicht klar, wie du die Antwort erhalten hast. Kannst du bitte deine gesamte Methode angeben, um die Antwort zu erhalten und in einer Datei zu speichern?

    – Raj

    3. Juli 13 um 6:35 Uhr

  • @Octavian Damiean Ich habe denselben Code verwendet und dann nie eine OutOfMemory-Ausnahme erhalten, aber ich bekomme jetzt org.xmlpull.v1.XmlPullParserException: unerwarteter Typ (Position:END_DOCUMENT null@1:1 in java.io.InputStreamReader@2fe6cc09) Ihre Hilfe wird für mich wie ein Diamant sein..

    – Arju

    17. November 16 um 12:34 Uhr

Zwei Strategien zur Lösung dieses Problems:

  1. Speichern Sie Ihren SOAP-XML-Stream beim Herunterladen direkt auf der Festplatte. Speichern Sie es nicht im Gedächtnis.
  2. Analysieren Sie es mit einem Parser im SAX-Stil, bei dem Sie nicht das gesamte DOM in den Speicher laden, sondern es in Blöcken parsen.

Abhängig von der Art von XML, mit der Sie arbeiten, ist die Verwendung von SAX-Parsern im Code normalerweise schwieriger. Sie müssen viele Dinge selbst im Auge behalten und können nicht von Abschnitt zu Abschnitt Ihres DOM-Baums “springen”. Aber der Speicherverbrauch wird viel geringer sein.

Beachten Sie jedoch, dass viele “High-Level”-Netzwerkkommunikationsbibliotheken normalerweise das gesamte XML-DOM in den Speicher laden, was hier der Fall sein könnte. Wahrscheinlich müssen Sie die HTTP-Verbindung selbst erstellen und verwalten und das Ergebnis dann manuell parsen.

  • Hallo Adrian, danke für die Antwort, ich habe das ausprobiert und es funktioniert gut, in diesem Fall ist die XML-Datei selbst etwa 8 MB groß. Bitte sehen Sie sich meine Antwort unten an, ich habe den Code auch eingefügt, um dies für die Zukunft zu tun

    – Mukul Jain

    13. Februar 11 um 10:37 Uhr


Fest!

Ich habe die Java-Klasse HttpTransportSE von hier heruntergeladen/kopiert (nach dem Kopieren können einige Codefehler auftreten, die jedoch alle schnell behoben werden können) und zu meinem Paket hinzugefügt:

https://github.com/mosabua/ksoap2-android/blob/master/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java

diese Zeile aus meiner Verbindungsklasse entfernt:

import org.ksoap2.transport.HttpsTransportSE;

und ersetzte diesen Code in meiner neuen HttpTransportSE.java-Datei:

if (debug) {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    byte[] buf = new byte[256];
                    while (true) {
                        int rd = is.read(buf, 0, 256);
                        if (rd == -1)
                            break;
                        bos.write(buf, 0, rd);
                    }
                    bos.flush();
                    buf = bos.toByteArray(); //Goes out of memory here
                    responseDump = new String(buf);
                    is.close();
                    is = new ByteArrayInputStream(buf);
    }

mit diesem

if (debug) {
    FileOutputStream bos = new FileOutputStream(file);

            byte[] buf = new byte[256];

            while (true) {
                int rd = is.read(buf, 0, 256);
                if (rd == -1) {
                    break;
                }
                bos.write(buf, 0, rd);
            }
            bos.flush();
}

wobei „file“ ein einfaches Dateiobjekt wie beispielsweise new File(“/sdcard/“, „myFile.xml“) ist

  • Ich habe dies in ksoap2-android 3.0.0-RC.5-SNAPSHOT implementiert. Könnten Sie bitte versuchen, ob es für Sie funktioniert?

    – Manfred Moser

    12. November 12 um 19:38 Uhr

  • Ok, ich werde versuchen, das Ergebnis so schnell wie möglich zu posten

    – Königshomer

    13. November 12 um 18:05 Uhr

  • @kinghomer Ich habe denselben Code verwendet und dann nie eine OutOfMemory-Ausnahme erhalten, aber ich bekomme jetzt org.xmlpull.v1.XmlPullParserException: unerwarteter Typ (Position: END_DOCUMENT null@1:1 in java.io.InputStreamReader@2fe6cc09) Ihre Hilfe wird Sei wie ein Diamant für mich..

    – Arju

    17. November 16 um 12:35 Uhr

.

502020cookie-checkSehr große SOAP-Antwort – Android – Fehler aufgrund unzureichenden Arbeitsspeichers

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy