Очень большой ответ 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" ), например