Очень большой ответ SOAP — Android — ошибка нехватки памяти

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

Данные имеют размер более 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);

}

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

Спасибо и с уважением Мукул


person Mukul Jain    schedule 09.02.2011    source источник
comment
Я думаю, что vtd-xml, несомненно, может помочь снизить использование памяти.   -  person vtd-xml-author    schedule 13.03.2011


Ответы (3)


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

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

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

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

person Adrian Kosmaczewski    schedule 13.02.2011
comment
Привет, Адриан, спасибо за ответ, я попробовал это, и это работает хорошо, в этом случае сам файл XML имеет размер около 8 МБ. Пожалуйста, смотрите мой ответ ниже, я также вставил код, чтобы сделать это, для дальнейшего использования - person Mukul Jain; 13.02.2011

Просто обновление, я обнаружил, что у метода «вызова» в 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 и записывает его в БД.

person Mukul Jain    schedule 13.02.2011
comment
Как вы использовали это с SoapObject в SoapSerializationEnvelope? Можете ли вы показать весь свой пример с вызовом веб-службы с использованием ksoap2 и записью результатов в файл вместо их сериализации в SoapObject? - person Sarah Vessels; 06.10.2011
comment
Спасибо, Мукул, за ваш ответ. Однако неясно, как вы получили ответ. Не могли бы вы предоставить весь метод получения ответа и его сохранения в файл. Как насчет вызова веб-службы makeCall (envelope, methodName, soapAction);? - person Raj; 03.07.2013
comment
@Octavian Damian Я использовал тот же код и никогда не получал исключение OutOfMemory, но теперь я получаю org.xmlpull.v1.XmlPullParserException: неожиданный тип (позиция:END_DOCUMENT null@1:1 в java.io.InputStreamReader@2fe6cc09) Ваша помощь будет для меня бриллиантом.. - person Araju; 17.11.2016

Исправлена!

Я скачал/скопировал 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»)

person kinghomer    schedule 04.10.2012
comment
Я реализовал это в ksoap2-android 3.0.0-RC.5-SNAPSHOT. Не могли бы вы попробовать, если это работает для вас? - person Manfred Moser; 12.11.2012
comment
Хорошо, я постараюсь и отпишусь о результате как можно скорее - person kinghomer; 13.11.2012
comment
@kinghomer Я использовал тот же код и никогда не получал исключение OutOfMemory, но теперь я получаю org.xmlpull.v1.XmlPullParserException: неожиданный тип (позиция: END_DOCUMENT null@1:1 в java.io.InputStreamReader@2fe6cc09) Ваша помощь будет будь для меня бриллиантом.. - person Araju; 17.11.2016