Очень большой ответ SOAP - ошибка при выходе из памяти Android

У меня есть приложение, в котором мне нужно загрузить большой объем данных через SOAP-вызов веб-сервису в приложение, когда оно выполняется в первый раз. Затем ответ отправляется в функцию, которая преобразует XML и сохраняет данные в файле db.

Данные имеют размер более 16 МБ, и каждый раз я получаю java.lang.OutOfMemoryError.

Изменение веб-службы для выдачи меньших объемов данных не является вариантом.

Есть ли способ загрузить большие данные? Возможно, что-то вроде InputStream?

Это мой код

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);

}

Кто-нибудь может предложить, что делать в этом случае?

Спасибо и приветствую Mukul

Ответ 1

Две стратегии, которые помогут вам решить эту проблему:

  • Сохраните XML-поток SOAP прямо на диске при его загрузке. Не храните его в памяти.
  • Разбирайте его с помощью синтаксического анализатора SAX, где вы не загружаете всю DOM в память, а скорее разбираете ее в кусках.

В зависимости от вида XML, который вы обрабатываете, использование синтаксических анализаторов SAX обычно сложнее в коде; вам придется самому отслеживать многие вещи, и вы не сможете "прыгать" из раздела в раздел вашего дерева DOM. Но потребление памяти будет ниже.

Обратите внимание, однако, что многие "высокоуровневые" сетевые коммуникационные библиотеки обычно загружают всю XML DOM в память, что может быть здесь. Вам, вероятно, придется самостоятельно создавать и управлять HTTP-соединением, а затем вручную анализировать результат.

Ответ 2

Просто обновление, я обнаружил, что метод "вызова" в AndroidHttpTransport заканчивается в этой строке -

           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);

вызов toByteArray занимает много памяти, поэтому, чтобы преодолеть это, вместо преобразования ответа в массив байтов, я теперь непосредственно пишу его в XML файл, и это сохраняется в моем местоположении. Здесь -

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();
}

У устройства больше не хватает памяти, и у меня есть собственный метод анализа, который берет этот XML и записывает его в БД.

Ответ 3

Фиксированный!

Я скачал/скопировал здесь java-класс HttpTransportSE (после копирования некоторые ошибки кода могут возникать, но все они быстро исправляются) и добавлены в мой пакет:

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

удалить из моего класса Connection следующую строку:

import org.ksoap2.transport.HttpsTransportSE;

и заменил этот код в моем новом файле HttpTransportSE.java:

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);
    }

с этим

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();
}

где "файл" - это простой файловый объект, такой как новый файл ( "/sdcard/", "myFile.xml" ), например