我正在开展一个项目,我正在通过互联网下载数据包并在我的应用程序中解析它们。每个数据包都有自己的结构,一开始我只收到文本数据。但是在某些时候我开始得到包含图像和这种东西的二进制数据包,有时在某些设备上,当二进制文件太大时,我得到了这个错误。实际上,在我测试时它永远不会发生,但我正在收到用户的一些报告。实际上我是这样做的:
阅读回复:
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等的图像。
所以任何想法如何摆脱这个问题。我不确定我能用什么而不是这个。
提前致谢!
答案 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中几乎被禁用,如果你想防范某些内容,请检查并抛出异常。)