Android字符串concat给出outofmemoryerror

时间:2012-01-31 08:24:28

标签: android httpurlconnection

我正在开展一个项目,我正在通过互联网下载数据包并在我的应用程序中解析它们。每个数据包都有自己的结构,一开始我只收到文本数据。但是在某些时候我开始得到包含图像和这种东西的二进制数据包,有时在某些设备上,当二进制文件太大时,我得到了这个错误。实际上,在我测试时它永远不会发生,但我正在收到用户的一些报告。实际上我是这样做的:

阅读回复:

InputStream response = new BufferedInputStream(connection.getInputStream());

        int bytesRead = -1;
        byte[] buffer = new byte[30 * 1024];
        while ((bytesRead = response.read(buffer)) > 0 && stopThread) {
            byte[] buffer2 = new byte[bytesRead];
            System.arraycopy(buffer, 0, buffer2, 0, bytesRead);
            handleDataFromSync(buffer2);
        }

并解析这样的数据:

public void handleDataFromSync(byte[] buffer) {
    RPCPacket packet;
    String responseBody;

    while(!stopThread) return;
    try {
        responseBody = new String(buffer, "UTF-8");
        StringBuilder tmp = new StringBuilder(responseBody);
        totalBytesReceived += responseBody.length();

        if (tmpBuffer != null) {
            tmpBuffer = tmpBuffer.append(tmp);
            tmp = tmpBuffer;
        }
        int bufferLen = tmp.length();
        int lastLoc = 0;
        boolean gotPacket;
        boolean gotField;
        String thisPart = "";

        try {
            do {
                gotPacket = false;
                gotField = true;
                int needsSize = packetFieldSizes[tmpCurrentField - 1];
                if (tmpCurrentField == packetFieldSizes.length) {
                    needsSize = payloadSize;
                }
                if (needsSize > bufferLen - lastLoc) {
                    gotField = false;
                    String proba = tmp.substring(lastLoc);
                    tmpBuffer =  new StringBuilder(proba);
                    break;
                }
                thisPart = tmp.substring(lastLoc, lastLoc + needsSize);
                lastLoc += needsSize;
                if (gotField) {

                    switch (tmpCurrentField) {

                    case 1: {
                        long intVal = Long.parseLong(thisPart);
                        objectIdentificator = (int) intVal;
                        break;
                    }
                    case 2: {
                        long intVal = Long.parseLong(thisPart);
                        if (intVal == 0) {
                            isBad = true;
                            break;
                        }
                        pType = (short) intVal;
                        break;
                    }
                    case 3: {
                        long intVal = Long.parseLong(thisPart);
                        if (intVal == 0) {
                            isBad = true;
                            break;
                        }
                        operationType = (short) intVal;
                        break;
                    }
                    case 4: {
                        objectOId = thisPart;
                        break;
                    }
                    case 5: {
                        long intVal = Long.parseLong(thisPart);
                        if (intVal == 0) {
                            isBad = true;
                            break;
                        }
                        id = (int) intVal;
                        break;
                    }
                    case 6: {
                        long intVal = Long.parseLong(thisPart);
                        payloadSize = (int) intVal;
                        dataSize = (int) intVal;
                        break;
                    }
                    case 7: {
                        hashH = thisPart;
                        break;
                    }
                    case 8: {
                        long intVal = Long.parseLong(thisPart);
                        if (intVal == 0) {
                            isBad = true;
                            break;
                        }
                        dataType = (short) intVal;
                        break;
                    }
                    case 9: {
                        if (payloadSize != 0) {
                            byte[] tmpData = Base64.decode(thisPart);
                            first = tmpData;
                        }
                        break;
                    }
                    }

                    if (tmpCurrentField >= packetFieldSizes.length)
                        gotPacket = true;

                    if (gotPacket) {
                        Log.d("", "Gotpacket!");
                        packet = new RPCPacket(objectIdentificator,
                                RPCPacketType.getPacketTypeByValue(pType),
                                RPCOperationType.getByValue(operationType),
                                objectOId, id, dataSize, hashH,
                                RPCPacketDataType.getByValue(dataType),
                                first);
                        parseRPCPacket(packet);

                        myProgress++;
                        update();
                        Log.e("","myProgress : "+myProgress);
                        Log.e("","TOTAL PACKETS : "+RPCCommunicator.totalPackets);

                        // release temp fields
                        objectIdentificator = 0;
                        pType = 0;
                        operationType = 0;

                        objectOId = null;

                        id = 0;
                        dataSize = 0;

                        hashH = null;

                        dataType = 0;

                        first = null;

                        tmpCurrentField = 1;
                        payloadSize = 0;

                    } else {
                        tmpCurrentField++;
                    }
                }

                // you baad bad buffer
                assert (lastLoc <= bufferLen);

                if (isBad)
                    break;

            } while (true);

        } catch (IOException e) {
            e.printStackTrace();
            RPCCommunicator.writeLogs(e.toString(), "Synchronization" ,"handleDataFromSync");

        } finally {
            thisPart = null;
            tmp = null;
        }
    } catch (Exception e) {
        e.printStackTrace();
        RPCCommunicator.writeLogs(e.toString(), "Synchronization","handleDataFromSync");
    }

}

所以只有在图像太大并且将字符串连接几次才能得到整个数据包时,错误才会出现在该行中:tmpBuffer = tmpBuffer.concat(tmp);。我正在阅读30KB的回复,但我可以收到300,400KB等的图像。

所以任何想法如何摆脱这个问题。我不确定我能用什么而不是这个。

提前致谢!

2 个答案:

答案 0 :(得分:3)

就像Jens所说,如果你需要来自不同线程的同步调用,只需使用StringBuilder(http://developer.android.com/reference/java/lang/StringBuilder.html)或StringBuffer(http://developer.android.com/reference/java/lang/StringBuffer.html)。

在当前代码中,每次将某些内容连接到String时,都会创建一个新对象,因此可能会出现内存问题。使用StringBuilder时,只使用一个Object。

答案 1 :(得分:1)

因此,对于启动器,您读取流的方式会分配太多内存,特别是此部分:

byte[] buffer2 = new byte[bytesRead];
System.arraycopy(buffer, 0, buffer2, 0, bytesRead);
handleDataFromSync(buffer2);

考虑修改handleDataFromSync(byte[] buffer)handleDataFromSync(byte[] buffer, int start, int count)的API,并在您阅读流时这样做:

while ((bytesRead = response.read(buffer)) > 0 && stopThread) {
    handleDataFromSync(buffer, 0, bytesRead);
}

您可以使用new String(buffer, offset, count, "UTF-8")中的handleDataFromSync创建一个字符串(如果您包含 any <,则只能以这种方式将字节转储到字符串中,不会产生正确解码的字符串/ b>在UTF-8中使用多个八位位组的字符,例如ÅÄÖ或其他垃圾。

你的解析中的字符串处理有点太模糊不清 - 你是否测量/打印了例如解析时tmpBuffer增长了多少?

在你的情况下,我会考虑使用一个InputStreamReader,并尝试完全修改解析 - 你有什么理由要读取30kb的块吗?

仅供参考assert(..)在Android中几乎被禁用,如果你想防范某些内容,请检查并抛出异常。)