studio - Respuesta SOAP muy grande-Android-error de memoria
ksoap2-android (3)
Tengo una aplicación donde necesito descargar una gran cantidad de datos a través de una llamada SOAP a un servicio web en la aplicación cuando se ejecuta por primera vez. La respuesta se envía a una función que convierte el XML y almacena los datos en un archivo db.
Los datos tienen más de 16 MB de tamaño y tengo java.lang.OutOfMemoryError cada vez.
La modificación del servicio web para proporcionar cantidades menores de datos no es una opción.
¿Hay alguna manera de poder descargar los datos de gran tamaño? Algo como un InputStream tal vez?
Este es mi código
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);
}
¿Alguien puede sugerir qué se debe hacer en este caso?
Gracias y saludos Mukul
Dos estrategias para ayudarlo a resolver este problema:
- Guarde su secuencia XML SOAP directamente en el disco mientras la descarga. No lo guarde en la memoria.
- Analícelo con un analizador de estilo SAX, donde no carga todo el DOM en la memoria, sino más bien analízalo en fragmentos.
Dependiendo del tipo de XML que maneje, el uso de analizadores SAX suele ser más difícil en el código; tendrá que hacer un seguimiento de muchas cosas usted mismo, y no podrá "saltar" de una sección a otra de su árbol DOM. Pero el consumo de memoria será mucho menor.
Sin embargo, tenga en cuenta que muchas bibliotecas de comunicación de red de "alto nivel" generalmente cargan todo el XML DOM en la memoria, lo que podría ser el caso aquí. Probablemente tendrá que crear y administrar la conexión HTTP usted mismo, y luego analizar manualmente el resultado.
Solo una actualización, encontré que el método de "llamada" en AndroidHttpTransport se estaba quedando sin memoria en esta línea:
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);
la llamada a toByteArray requiere mucha memoria, así que para superar esto, en lugar de convertir la respuesta a una matriz de bytes, ahora la escribo directamente en un archivo XML, y esta se guarda en la ubicación que elija. Aquí -
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();
}
El dispositivo ya no se queda sin memoria, y tengo un método de análisis personalizado que toma este XML y lo escribe en el DB.
¡Fijo!
Descargué / copié la clase de HttpTransportSE java desde aquí (después de copiar, pueden ocurrir algunos errores de código, pero todos son rápidos de reparar) y agregué a mi paquete:
https://github.com/mosabua/ksoap2-android/blob/master/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
eliminado de mi clase de conexión esta fila:
import org.ksoap2.transport.HttpsTransportSE;
y sustituí este código en mi nuevo archivo 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);
}
con este
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();
}
donde "archivo" es un objeto de archivo simple como nuevo archivo ("/ sdcard /", "myFile.xml") por ejemplo