非常大的SOAP响应-Android内存不足错误

10

我有一个应用程序,需要在首次运行时通过SOAP调用从Web服务下载大量数据到应用程序中。然后将响应发送到将XML转换并将数据存储在数据库文件中的函数。

数据超过16MB大小,每次都会出现java.lang.OutOfMemoryError错误。

修改Webservice以提供较小量的数据不是一个选项。

是否有一种方法可以下载大型数据?例如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


我认为vtd-xml无疑可以帮助降低内存使用率。 - vtd-xml-author
3个回答

6

更新一下,我发现 AndroidHttpTransport 类中的 "call" 方法在这一行出现了内存不足的问题:

           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
您如何在SoapSerializationEnvelope中使用SoapObject?能否展示一个完整的ksoap2调用Web服务并将结果写入文件的示例,而不是将其序列化为SoapObject - Sarah Vessels
感谢Mukul的回答。然而,您是如何获得响应并将其保存到文件中的并不清楚。能否请您提供获取响应并保存到文件的完整方法?那么调用webservice makeCall(envelope, methodName, soapAction)呢? - Raj
我之前使用了相同的代码,从未遇到过内存不足异常,但现在却出现了 org.xmlpull.v1.XmlPullParserException: unexpected type (position:END_DOCUMENT null@1:1 in java.io.InputStreamReader@2fe6cc09)。 你的帮助对我而言就像钻石一样珍贵。 - Araju

5

以下是两种帮助您解决此问题的策略:

  1. 将SOAP XML流直接保存到磁盘中,而不是存储在内存中。
  2. 使用SAX风格的解析器进行解析,其中您不会将整个DOM加载到内存中,而是分块解析。

根据您处理的XML类型,使用SAX解析器通常在代码方面更加困难;您将不得不自己跟踪许多内容,并且无法从DOM树的一个部分“跳转”到另一个部分。但是,内存消耗将大大降低。

请注意,然而,许多“高级”网络通信库通常会将整个XML DOM加载到内存中,这可能是这里的情况。您可能需要自己创建和管理HTTP连接,然后手动解析结果。


1
嗨Adrian,谢谢回复,我尝试了这个方法,它很有效,在这种情况下,XML文件本身大小约为8 MB。请看下面的回答,我已经粘贴了代码,以备将来参考。 - Mukul Jain

1

问题已解决!

我从这里下载/复制了HttpTransportSE Java类(复制后可能会出现一些代码错误,但都很容易修复),并将其添加到我的包中:

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

我从我的连接类中删除了这一行:

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

其中 "file" 是一个简单的文件对象,例如 new File("/sdcard/","myFile.xml")


1
我已经在ksoap2-android 3.0.0-RC.5-SNAPSHOT中实现了这个。请问您能否尝试一下看是否可行? - Manfred Moser
好的,我会尽快尝试并发布结果。 - kinghomer
@kinghomer,我之前使用了相同的代码,从未遇到过OutOfMemory异常,但现在出现了org.xmlpull.v1.XmlPullParserException: unexpected type (position:END_DOCUMENT null@1:1 in java.io.InputStreamReader@2fe6cc09)。你的帮助对我来说就像钻石一样珍贵。 - Araju

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接