【问题标题】:Java/Android: java.lang.OutOfMemoryError while building a JSON objectJava/Android:构建 JSON 对象时出现 java.lang.OutOfMemoryError
【发布时间】:2012-03-21 06:21:37
【问题描述】:

我正在从公共数据库 URI http://data.seattle.gov/api/views/3k2p-39jp/rows.json 导入 JSON 数据,行数达到 445454。使用以下代码,我正在构建整个数据的 JSON 对象。

   HttpGet get = new HttpGet(uri);
   HttpClient client = new DefaultHttpClient();
   HttpResponse response = client.execute(get);
   BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
    StringBuilder builder=new StringBuilder();
for(String line=null;(line = reader.readLine()) != null;){
      builder.append(line).append("\n");
    }
  JSONTokener jsonTokener=new JSONTokener(builder.toString());
  JSONObject finalJson=new JSONObject(jsonTokener);
  JSONArray data=finalJson.getJSONArray("data");

由于数据太大,我得到03-21 03:41:49.714: E/AndroidRuntime(666): Caused by: java.lang.OutOfMemoryError 将错误源指向buildr.append(line).append("\n")。无论如何我可以处理大型数据集而不会出现内存分配问题?

【问题讨论】:

  • 阅读部分内容而不是全部内容.. ?

标签: java android json webrequest


【解决方案1】:

JSON 是巨大的!

您肯定需要使用流式 JSON 解析器。 Android 有两个:GSON 和 Jackson。

GSON Streaming 解释于:https://sites.google.com/site/gson/streaming

我喜欢 GSON 如何解释您遇到的问题:

大多数应用程序应该只使用对象模型 API。 JSON 流仅在几种情况下有用:

当不可能或不希望将整个对象模型加载到内存中时。这在内存有限的移动平台上最为相关。

Jackson Streaming 记录在:http://wiki.fasterxml.com/JacksonInFiveMinutes#Streaming_API_Example

【讨论】:

  • 我喜欢 GSON。所以你是说我应该使用JSONReaderList<Message> 而不是使用BufferedReaderList<Message> 来解决内存问题?
  • 正确。 JSONReader 将接收您的 inputStream,因此它将替换代码中的 BufferedReader 和 StringBuilder。请注意,Message 是一个示例类。查看该 JSON,您可能希望将其替换为您自己的 PoliceResponse 类。由于 JSON 不仅仅是示例中的一个大数组,因此您必须先导航到包含警方响应的数组,然后才能开始遍历该数组。
【解决方案2】:

如果可能,只请求部分数据 - 这也减少了网络 io 的时间,从而节省了电池。

否则,您可以尝试不将传入的数据保留在内存中,而是将其“流式传输”到 sd 卡上。当它存储在那里时,您可以对其进行迭代。这很可能意味着使用您自己的 JSON 标记器,它不会构建完整的树,但能够(如 SAX 解析器)一次只查看对象树的一部分。

你可以看看Jackson,它有流模式,可能适用。

【讨论】:

    【解决方案3】:

    流式拉解析器是一种方式。我推荐 GSON,因为它的内存占用很小(拉解析大约 16K,jackson 更大)

    你的代码有问题,因为你分配了:

    • 缓冲区保存来自服务的所有字符串数据
    • 所有 JSON DOM 对象

    这很慢,会让你记忆崩溃。

    如果您需要 JSON 数据中的 java 对象,您可以尝试我在 GSON 上构建的小型数据绑定库(羞辱自我广告):

    https://github.com/ko5tik/jsonserializer

    【讨论】:

      【解决方案4】:

      我的做法有点不同,我的 JSON 代码正在等待状态,该状态即将结束。所以我修改了代码提前返回。

      // try to get formattedAddress without reading the entire JSON
              String formattedAddress;
              while ((read = in.read(buff)) != -1) {
                  jsonResults.append(buff, 0, read);
                  formattedAddress = ((String) ((JSONObject) new JSONObject(
                          jsonResults.toString()).getJSONArray("results").get(0))
                          .get("formatted_address"));
                  if (formattedAddress != null) {
                      Log.i("Taxeeta", "Saved memory, returned early from json") ;
                      return formattedAddress;
                  }               
              }
      JSONObject statusObj = new JSONObject(jsonResults.toString());
              String status = (String) (statusObj.optString("status"));
              if (status.toLowerCase().equals("ok")) {
                  formattedAddress = ((String) ((JSONObject) new JSONObject(
                          jsonResults.toString()).getJSONArray("results").get(0))
                          .get("formatted_address"));
                  if (formattedAddress != null) {
                      Log.w("Taxeeta", "Did not saved memory, returned late from json") ;
                      return formattedAddress;
                  }           
              } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-09
        • 2019-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-06
        相关资源
        最近更新 更多