检索大量数据时出现Android OutOfMemoryError

时间:2014-06-05 09:00:07

标签: android stream out-of-memory

我有一个应用程序可以进行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。我已经用Google搜索了错误,这可能是由于StringBuilder中的内存中存储了太多数据造成的。

作为我已阅读的解决方法,我可以将Stream直接读入字符串以避免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

有没有人知道如何在不导致此错误的情况下将此Stream读入字符串?

提前致谢。

4 个答案:

答案 0 :(得分:2)

  

作为我已阅读的解决方法,我可以将Stream直接读入字符串以避免StringBuilder。

这没有用。在封面下方,CharStreams方法最有可能使用StringBuilder或等效...并且因为它无法知道构建器需要多大,所以它将使用构建器&& #34;双的尺寸"扩展支持阵列的策略。

将数据读入内存的最有效内存的方法是预先分配某种缓冲区(StringBuilderbyte[]char[]等),其大小为大到足以保存字符/字节,然后填充缓冲区。如果需要将缓冲区内容转换为字符串,则仍需要两倍的内存。

但这只能避免不可避免的扩展问题。最终,XML将无法容纳在内存中。您真正需要做的是将代码更改为不尝试将整个XML文档保存在内存中。您可以执行以下操作之一:

  • 将其提供给事件驱动的XML解析器(例如SAX)以提取感兴趣的信息,或
  • 一次将其写入本地文件,字节,字符,行或缓冲区。

答案 1 :(得分:0)

我认为问题出在新的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
  }
}

答案 2 :(得分:0)

你确定你的数据那么大吗? XML的大小是多少?
无论如何,您必须在光盘(首选项,sqlite或文件)上兑现您的数据,然后按块读取或解析缓存的数据块。

答案 3 :(得分:0)

你可以使用deflater和inflater压缩数据,或者使用\ n和printwriter从服务器发送字符串,然后逐行将数据写入clinet上的临时文件