2012-03-21 5 views
8

Sto importando i dati JSON da un URI di database pubblico http://data.seattle.gov/api/views/3k2p-39jp/rows.json e le righe arrivano fino al 445454. Utilizzando il seguente codice Sto costruendo l'oggetto JSON dell'intero dati.Java/Android: java.lang.OutOfMemoryError durante la creazione di un oggetto 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"); 

Poiché i dati è troppo grande, sto ottenendo 03-21 03:41:49.714: E/AndroidRuntime(666): Caused by: java.lang.OutOfMemoryError indicando la fonte di errore in buildr.append(line).append("\n"). Posso comunque gestire grandi set di dati senza problemi di allocazione della memoria?

+0

leggere parti di esso invece dell'intera cosa ..? – JoxTraex

risposta

5

Quel JSON è enorme!

È assolutamente necessario utilizzare un parser JSON in streaming. Ce ne sono due là fuori per Android: GSON e Jackson.

GSON Streaming è spiegato in: https://sites.google.com/site/gson/streaming

Mi piace come GSON spiega il problema che stai avendo:

La maggior parte delle applicazioni dovrebbero utilizzare solo l'oggetto del modello API. Lo streaming JSON è utile in alcune situazioni:

Quando è impossibile o indesiderato caricare in memoria l'intero modello di oggetto. Questo è più rilevante sulle piattaforme mobili in cui la memoria è limitata.

Jackson Streaming è documentato: http://wiki.fasterxml.com/JacksonInFiveMinutes#Streaming_API_Example

+0

Mi piace il GSON. Quindi stai dicendo che invece di usare 'BufferedReader' e' StringBuilder' dovrei fare 'JSONReader' e' List 'per avere un vantaggio sui problemi di memoria? – jmishra

+0

corretto. JSONReader prenderà il tuo inputStream, in modo da sostituire sia BufferedReader che StringBuilder nel tuo codice. Nota che Message è un esempio di classe. Guardando a quel JSON, probabilmente vorrai sostituirlo con la tua classe PoliceResponse. Dato che il JSON non è solo un grande array come negli esempi, dovrai prima accedere alla matrice che contiene le risposte della polizia, quindi puoi iniziare a scorrere attraverso quella matrice. – louielouie

1

Se possibile, richiedere solo parti dei dati - questo riduce anche il tempo per la rete io e quindi risparmia batteria.

Altrimenti si potrebbe provare a non tenere i dati in arrivo in memoria, ma a 'riversarli' sulla sd-card. Quando è memorizzato lì puoi quindi scorrere su di esso. Molto probabilmente questo significherà usare il tuo tokenizzatore JSON che non costruisce un albero completo, ma che è in grado (come un parser SAX) di guardare solo una parte dell'albero degli oggetti alla volta.

Si può dare un'occhiata a Jackson, che ha una modalità di streaming, che può essere applicabile.

1

Streaming tirare parser è la strada. Mi raccomando GSON, in quanto questo ha piccolo footpring memoria (basta tirare l'analisi è di circa 16K, Jackson è il modo più grande)

Il codice è problematico perché si alloca:

  • buffer per contenere tutti i dati di stringa provenienti da servizio
  • tutti gli oggetti JSON DOM

e questo è lento, e ti dà la memoria tracollo.

Nel caso abbiate bisogno di oggetti Java su dati JSON, si può provare il mio piccolo edificio della biblioteca associazione dati sul GSON (shameles auto pubblicità off):

https://github.com/ko5tik/jsonserializer

0

ho fatto un po 'diverso, il mio Il codice JSON stava aspettando lo stato, che arriva verso la fine. Così ho modificato il codice per tornare prima.

// 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; 
      }   
     }