我有一个应用程序,使一个web调用和检索XML数据。如果没有太多的数据,下面的代码可以正常工作。
public class WebClient {
private static final String TAG = WebClient.class.getSimpleName();
private String result;
private static String convertStreamToString(InputStream is) {
/*
* To convert the InputStream to String we use the
* BufferedReader.readLine() method. We iterate until the BufferedReader
* return null which means there's no more data to read. Each line will
* appended to a StringBuilder and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
public String connect(String url) {
Log.e(TAG, "inside LoginWebClient.connect(url). url = " + url);
result = null;
HttpClient httpclient = new DefaultHttpClient();
// Prepare a request object
HttpGet httpget = new HttpGet(url);
// Execute the request
HttpResponse response;
try {
response = httpclient.execute(httpget);
// Examine the response status
Log.i(TAG, response.getStatusLine().toString());
// Get hold of the response entity
HttpEntity entity = response.getEntity();
// If the response does not enclose an entity, there is no need
// to worry about connection release
if (entity != null) {
InputStream instream = entity.getContent();
result = convertStreamToString(instream);
//Log.i(TAG, result);
// Closing the input stream will trigger connection release
instream.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e(TAG, "inside WebClient.connect(url). result = " + result);
return result;
}//end of connect
.
如果应用程序使web调用返回大量数据,我得到一个OutOfMemoryError。我已经在谷歌上搜索了这个错误,它可能是由于在StringBuilder的内存中持有过多的数据引起的。
作为一个解决方案,我已经读过,我可以读取流直接到一个字符串,以避免StringBuilder。
我导入了com.google.common.io.CharStreams jar文件到我的项目中,并尝试了以下操作:
private static String convertStreamToStringForGetAlerts(InputStream is) {
String stringFromStream = null;
try {
stringFromStream = CharStreams.toString(new InputStreamReader(is, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stringFromStream;
}
但我仍然得到以下错误:
06-05 09:44:40.809: E/AndroidRuntime(5851): FATAL EXCEPTION: AsyncTask #2
06-05 09:44:40.809: E/AndroidRuntime(5851): java.lang.RuntimeException: An error occured while executing doInBackground()
06-05 09:44:40.809: E/AndroidRuntime(5851): at android.os.AsyncTask$3.done(AsyncTask.java:278)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.lang.Thread.run(Thread.java:856)
06-05 09:44:40.809: E/AndroidRuntime(5851): Caused by: java.lang.OutOfMemoryError
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:94)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:162)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.lang.StringBuilder.append(StringBuilder.java:311)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.lang.StringBuilder.append(StringBuilder.java:44)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.google.common.io.CharStreams.copy(CharStreams.java:204)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.google.common.io.CharStreams.toStringBuilder(CharStreams.java:245)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.google.common.io.CharStreams.toString(CharStreams.java:219)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.carefreegroup.rr3.carefreeoncall.WebClient.convertStreamToStringForGetAlerts(WebClient.java:151)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.carefreegroup.rr3.carefreeoncall.WebClient.connectForGetAlerts(WebClient.java:198)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.carefreegroup.rr3.carefreeoncall.WebService.getAlerts(WebService.java:1778)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.carefreegroup.rr3.carefreeoncall.ShowAlertsActivity$AsyncGetAlerts.doInBackground(ShowAlertsActivity.java:88)
06-05 09:44:40.809: E/AndroidRuntime(5851): at com.carefreegroup.rr3.carefreeoncall.ShowAlertsActivity$AsyncGetAlerts.doInBackground(ShowAlertsActivity.java:1)
06-05 09:44:40.809: E/AndroidRuntime(5851): at android.os.AsyncTask$2.call(AsyncTask.java:264)
06-05 09:44:40.809: E/AndroidRuntime(5851): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
06-05 09:44:40.809: E/AndroidRuntime(5851): ... 4 more
有没有人有任何想法,我如何可以读取这个流成一个字符串,而不会导致这个错误?
作为一个解决方案,我已经读过,我可以读取流直接到一个字符串,以避免StringBuilder。
那没有用。在表面之下,CharStreams
方法很可能使用StringBuilder
或等效方法…由于它不知道构建器需要多大,它将使用构建器的"双倍大小"策略来扩展后备数组。
将数据读取到内存中最有效的方法是预先分配某种缓冲区(StringBuilder
, byte[]
, char[]
等),其大小足以容纳字符/字节,然后填充缓冲区。如果需要将缓冲区内容转换为String,则仍然需要两倍的内存量。
但这只是避免了不可避免的缩放问题。最终,XML将太大而无法存储在内存中。您真正需要做的是更改代码,使其不试图将整个XML文档保存在内存中。您可以执行以下操作之一:
- 将其提供给事件驱动的XML解析器(例如SAX)以提取感兴趣的信息,或者
- 将其写入本地文件,一次写入一个字节、字符、行或缓冲区。
我认为问题是在新的InputStreamReader(....)。为什么不在过程的中间应用字节转换呢?
public byte[] read(InputStream istream)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024]; // Experiment with this value
int bytesRead;
while ((bytesRead = istream.read(buffer)) != -1)
{
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
}
// after the process is run, we call this method with the String
public void readLines(byte[] data)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data)));
String line;
while ((line = reader.readLine()) != null)
{
// do stuff with line
}
}
你确定你的数据有那么大吗?XML的大小是多少?
无论如何,您必须将数据存入磁盘(首选项、sqlite或文件),然后逐个块读取或解析缓存的数据。
您可以使用deflater和inflater压缩数据,或者使用n和打印机从服务器发送字符串,然后将数据逐行写入客户机上的临时文件