2012-03-21 50 views
8

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

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.OutOfMemoryErrorbuildr.append(line).append("\n")指向误差的来源。无论如何,我可以处理大型数据集而不会导致内存分配问题?

+0

阅读它的部分,而不是整个事情..? – JoxTraex 2012-03-21 06:24:38

回答

5

JSON很大!

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

GSON流解释为:https://sites.google.com/site/gson/streaming

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

大多数应用程序只使用对象模型API。 JSON流式传输在以下几种情况下很有用:

当将整个对象模型加载到内存中是不可能或不可取的。这与内存有限的移动平台最为相关。

杰克逊流被记录在:http://wiki.fasterxml.com/JacksonInFiveMinutes#Streaming_API_Example

+0

我喜欢GSON。所以你说的不是使用'BufferedReader'和'StringBuilder'我应该使用'JSONReader'和'List '来优先于内存问题? – jmishra 2012-03-21 07:05:30

+0

正确。 JSONReader将接受你的inputStream,以便在代码中替换BufferedReader和StringBuilder。请注意,消息是一个示例类。看着那个JSON,你可能会想用你自己的PoliceResponse类代替它。由于JSON不仅仅是例子中的一个大数组,因此您必须首先导航到包含警察响应的数组,然后才能开始迭代该数组。 – louielouie 2012-03-21 07:16:59

1

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

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

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

1

流pull解析器是这样的。我建议GSON,因为这有小内存footpring(只拉解析约为16K,杰克逊更强壮)

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

  • 缓冲区来容纳所有的字符串数据来自何处服务
  • 所有的JSON DOM对象

,这是缓慢的,并给你的内存崩溃。

情况下你需要Java对象你的JSON数据,您可以尝试在GSON我小的数据绑定的图书馆大楼(shameles自广告关闭):

https://github.com/ko5tik/jsonserializer

0

我做了一个有点不同,我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; 
      }   
     }