Autore Topic: Oggetto HTTP response troppo grande  (Letto 2165 volte)

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Oggetto HTTP response troppo grande
« il: 21 Marzo 2013, 22:33:01 CET »
0
Ciao a tutti, ho un problema nel metodo doInBackground(). In pratica, nella risposta HTTP, quando ho un array di oggetti JSON molto grande, ad esempio 6000, e vado a costruire una stringa con StringBuilder, ho un errore di tipo outOfMemory. Pensavo di utilizzare i parser JACKSON o GSON ma per ragioni varie non posso utilizzarli. Non posso neanche ottenere una risposta con meno risultati alla volta. A qualcuno viene in mente qualcosa?

Codice (Java): [Seleziona]
protected String doInBackground(Void... urls) {
                String st_response="";
                try {
                        final HttpResponse response;
                        //synchronized (httpClient) {
                                if(request==null)
                                         cancel(true);
                                String m_url;
                                if(isPost)
                                {
                                        m_url=baseUrl+script;
                                        HttpPost httppost = new HttpPost(m_url);
                                        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
                                    nameValuePairs.add(new BasicNameValuePair("id", "12345"));

                                    httppost.setEntity(new UrlEncodedFormEntity((List<NameValuePair>)request));
                                    if(getClient()==null)
                                    {
                                        return "";
                                    }
                                    response = getClient().execute(httppost);
                                       
                                }
                                else
                                {
                                        String m_request=request==null?"":Uri.encode(request.toString());
                                        if(DEBUG)
                                                m_url=baseUrl1+script+m_request;
                                        else
                                        {
                                                m_url=baseUrl+script+m_request;
                                        }
                                        Params.printLog(TAG,"run :"+m_url);
                                        HttpUriRequest request1=new HttpGet(m_url);
                                    if(getClient()==null || request1==null)
                                    {
                                        return "";
                                    }
                                        response = getClient().execute(request1);
                                }
                               

                                StringBuilder builder = new StringBuilder();
                                InputStream is=response.getEntity().getContent();
                                BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                                for (String line = null; (line = reader.readLine()) != null;) {

                                    builder.append(line + "\n");

                                   // builder.append(line).append("\n");
                                }
                                is.close();
                                st_response=builder.toString();
                               
                        //}
                } catch (ClientProtocolException e) {
                        e.printStackTrace();
                } catch (UnknownHostException e)
                {
                        st_response="{\"httperr\":1}";
                        e.printStackTrace();
                }
                catch (IOException e) {
                        st_response="{\"httperr\":1}";
                        e.printStackTrace();
                }
                return st_response;
        }

Codice (Java): [Seleziona]
03-21 17:30:46.873: E/AndroidRuntime(14828): FATAL EXCEPTION: AsyncTask #5
03-21 17:30:46.873: E/AndroidRuntime(14828): java.lang.RuntimeException: An error occured while executing doInBackground()
03-21 17:30:46.873: E/AndroidRuntime(14828):    at android.os.AsyncTask$3.done(AsyncTask.java:278)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.lang.Thread.run(Thread.java:856)
03-21 17:30:46.873: E/AndroidRuntime(14828): Caused by: java.lang.OutOfMemoryError
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:94)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:124)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.lang.StringBuilder.append(StringBuilder.java:271)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.io.BufferedReader.readLine(BufferedReader.java:417)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at com.qriket.qriket.httpclient.HttpAsyncTask.doInBackground(HttpAsyncTask.java:142)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at com.qriket.qriket.httpclient.HttpAsyncTask.doInBackground(HttpAsyncTask.java:1)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at android.os.AsyncTask$2.call(AsyncTask.java:264)
03-21 17:30:46.873: E/AndroidRuntime(14828):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
03-21 17:30:46.873: E/AndroidRuntime(14828):    ... 4 more

Il motivo è che il risultato è troppo grande per lo stringBuilder e quindi mi da errore..Ho provato anche in altri modi come questo:

Codice (Java): [Seleziona]
        int responseCode = response.getStatusLine().getStatusCode();
                                switch(responseCode) {
                                case 200:
                                HttpEntity entity = response.getEntity();
                                    if(entity != null) {
                                        st_response = EntityUtils.toString(entity);
                                    }
                                    break;
                                }

ma il risultato è sempre lo stesso.

Offline DarnellNajanReed

  • Utente normale
  • ***
  • Post: 359
  • Respect: +49
    • Google+
    • Mostra profilo
  • Dispositivo Android:
    LG Optimus One, Acer Iconia A500/501, Asus Transformer Prime, Galaxy ACE, Galaxy S Plus, Galaxy S Advance P, Galaxy Tab 2 7.0, Google Nexus 7
  • Play Store ID:
    Luigi Notaro
  • Sistema operativo:
    OS X 10.8.3
Re:Oggetto HTTP response troppo grande
« Risposta #1 il: 22 Marzo 2013, 00:28:26 CET »
0
Se il problema è che ciò che scarichi con la chiamata http non "sta" nella ram a disposizione (considerando anche che non puoi chiedere risultati parzai),
io proverei a salvare tutto su file, ovviamente senza costruirti prima lo stringone che fa esplodere tutto come sopra. Il file lo puoi poi leggere riga per riga, o comunque senza doverlo caricare in memoria per intero.

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Re:Oggetto HTTP response troppo grande
« Risposta #2 il: 22 Marzo 2013, 03:14:45 CET »
0
Cioè stai dicendo di salvare dentro doInBackground tutto il risultato in un file e dopodiché, sempre nello stesso metodo, aprire quel file, leggere una riga alla volta e inserirla nella stringa fino ad ottenere lo stringone??

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Oggetto HTTP response troppo grande
« Risposta #3 il: 22 Marzo 2013, 08:07:48 CET »
0
Fai anche qualche considerazione più generale sull'app che stai realizzando, perchè anche solo rischiare di andare OutOfmemory (se quella è la tipologia di dati che andrai a trattare) rende l'app a rischio di crash e/o di compatibilità con alcune tipologie di terminali in circolazione. Se le richieste http che usi lo supportano o comunque ne hai il controllo, puoi pensare ad un meccanismo di paginazione (come suggeriva anche Darnell).

Citazione
Cioè stai dicendo di salvare dentro doInBackground tutto il risultato in un file e dopodiché, sempre nello stesso metodo, aprire quel file, leggere una riga alla volta e inserirla nella stringa fino ad ottenere lo stringone??

Credo che Darnell intendesse altro:

Riprendendo il discorso di prima, quando raggiungi il muro dell'OutOfMemory il problema è sostanzialmente progettuale e occorre proprio cambiare approccio. Se devi gestire una gran mole di dati (OutOfMemory ti dice che è oltre le capacità del dispositivo), l'approccio è evitare di tenerli in memoria in un oggetto String/StringBuilder. Come suggerisce Darnell metti i dati su file e accedi di volta in volta alle parti che ti servono. Perchè se carichi tutto nello stringone sei punto a capo (magari ti risolve questo caso specifico, ma con size di 8000 torna a esplodere). La domanda che devi porti è: perchè ti serve caricare tutto in una String?


NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline DarnellNajanReed

  • Utente normale
  • ***
  • Post: 359
  • Respect: +49
    • Google+
    • Mostra profilo
  • Dispositivo Android:
    LG Optimus One, Acer Iconia A500/501, Asus Transformer Prime, Galaxy ACE, Galaxy S Plus, Galaxy S Advance P, Galaxy Tab 2 7.0, Google Nexus 7
  • Play Store ID:
    Luigi Notaro
  • Sistema operativo:
    OS X 10.8.3
Re:Oggetto HTTP response troppo grande
« Risposta #4 il: 22 Marzo 2013, 10:22:17 CET »
0
Citazione
Se devi gestire una gran mole di dati (OutOfMemory ti dice che è oltre le capacità del dispositivo), l'approccio è evitare di tenerli in memoria in un oggetto String/StringBuilder. Come suggerisce Darnell metti i dati su file e accedi di volta in volta alle parti che ti servono. Perchè se carichi tutto nello stringone sei punto a capo (magari ti risolve questo caso specifico, ma con size di 8000 torna a esplodere). La domanda che devi porti è: perchè ti serve caricare tutto in una String?
Esattamente quello che intendevo  :-)

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Re:Oggetto HTTP response troppo grande
« Risposta #5 il: 22 Marzo 2013, 15:22:05 CET »
0
Grazie innanzitutto per le risposte. Il problema è che nella mia app, ad esempio ho una lista di amici di 5000 persone. I dati di ogni persona sono in un oggetto di tipo JSON, quindi ho un JSONArray di 5000 oggetti. Dato che questi oggetti li metto in una listview, in qualche modo li devo prendere. Li prendo con uno StringBuffer e li metto dentro una stringa da cui poi prenderò i dati. Con questo meccanismo gestisco tutte le risposte del server, e solo in questo caso la mole di dati  molto alta. Non vorrei cambiare logica solo per un'attività.

Offline DarnellNajanReed

  • Utente normale
  • ***
  • Post: 359
  • Respect: +49
    • Google+
    • Mostra profilo
  • Dispositivo Android:
    LG Optimus One, Acer Iconia A500/501, Asus Transformer Prime, Galaxy ACE, Galaxy S Plus, Galaxy S Advance P, Galaxy Tab 2 7.0, Google Nexus 7
  • Play Store ID:
    Luigi Notaro
  • Sistema operativo:
    OS X 10.8.3
Re:Oggetto HTTP response troppo grande
« Risposta #6 il: 23 Marzo 2013, 00:10:13 CET »
0
Lo scenario che descrivi mi sembra poco adatto ad un ambiente mobile, dove l'ottimizzazione è tutto. L'approccio che descrivi (mettere 5000 oggetti in memoria) è scorretto di principio.

Citazione
Dato che questi oggetti li metto in una listview, in qualche modo li devo prendere
Ci sono modi più leggeri ed efficienti di quello che descrivi tu, però.
A naso, senza conoscere il resto del contesto, potresti per esempio travasare tutto in un DB e prendere i dati da lì.
O comunque orientarti verso una soluzione che non faccia un cattivo uso della memoria.

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3489
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
Re:Oggetto HTTP response troppo grande
« Risposta #7 il: 23 Marzo 2013, 11:23:40 CET »
0
Ma quanto è grande, in kb (o forse è meglio parlare in mb), questo json?

Comunque, hai provato a parsare il json direttamente dallo stream, senza andare prima a inserirlo in una stringa?

JsonReader | Android Developers

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Re:Oggetto HTTP response troppo grande
« Risposta #8 il: 23 Marzo 2013, 20:55:25 CET »
0
DarnellNajan i dati mi vengono forniti da un db in un array JSON e per leggerli, mi costruisco uno stringone con stringBuilder. Onestamente non so in mb quanto occupano, però so' che sono tra i 5000 e 6000 JSONObject, ognuno con 4 campi.

Offline DarnellNajanReed

  • Utente normale
  • ***
  • Post: 359
  • Respect: +49
    • Google+
    • Mostra profilo
  • Dispositivo Android:
    LG Optimus One, Acer Iconia A500/501, Asus Transformer Prime, Galaxy ACE, Galaxy S Plus, Galaxy S Advance P, Galaxy Tab 2 7.0, Google Nexus 7
  • Play Store ID:
    Luigi Notaro
  • Sistema operativo:
    OS X 10.8.3
Re:Oggetto HTTP response troppo grande
« Risposta #9 il: 23 Marzo 2013, 21:17:52 CET »
0
Citazione
DarnellNajan i dati mi vengono forniti da un db in un array JSON
Io intendevo un DB locale  :-)

Citazione
e per leggerli, mi costruisco uno stringone con stringBuilder
Questo era chiaro, ed è proprio il punto nodale della faccenda.

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Re:Oggetto HTTP response troppo grande
« Risposta #10 il: 23 Marzo 2013, 21:23:47 CET »
0
Stiamo pensando di ottenere solo un tot di JSONObject alla volta, un 500 e nel caso di scroll richiamare lo script con un indice diverso. Oltretutto, oltre problemi di Stringbuilder, va spesso outofmemory per il caricamento delle immagini di ogni profilo..

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Oggetto HTTP response troppo grande
« Risposta #11 il: 24 Marzo 2013, 09:12:11 CET »
0
Stiamo pensando di ottenere solo un tot di JSONObject alla volta, un 500 e nel caso di scroll richiamare lo script con un indice diverso. Oltretutto, oltre problemi di Stringbuilder, va spesso outofmemory per il caricamento delle immagini di ogni profilo..

Si, questa è la paginazione a cui accennavo prima. E' chiaramente la strada da intraprendere, visto che API blasonate come ad esempio le Google Spreadsheet API ne fanno largamente uso.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Re:Oggetto HTTP response troppo grande
« Risposta #12 il: 25 Marzo 2013, 15:44:53 CET »
0
Nel caso volessi intraprendere questa strada credo che sia meglio che l'ordinamento venga fatto direttamente sul server e inoltre, quest'ultimo, restituisca per default quando l'activity viene caricata i primi 500 elementi partendo da 0. Che ne dite?

Offline Nicola_D

  • Moderatore
  • Utente storico
  • *****
  • Post: 2479
  • SBAGLIATO!
  • Respect: +323
    • Github
    • Google+
    • nicoladorigatti
    • Mostra profilo
  • Dispositivo Android:
    Nexus 6p, Nexus 4, Nexus S, Nexus 7(2012)
  • Sistema operativo:
    Windows 7
Re:Oggetto HTTP response troppo grande
« Risposta #13 il: 25 Marzo 2013, 16:27:07 CET »
0
certo, altri modi sono un po complessi da gestire
IMPORTANTE:NON RISPONDO A PROBLEMI VIA MESSAGGIO PRIVATO
LOGCAT: Non sai cos'è? -> Android Debug Bridge | Android Developers
               Dov'è in Eclipse? -> Window -> Open Prospective -> DDMS e guarda in basso!
[Obbligatorio] Logcat, questo sconosciuto! (Gruppo AndDev.it LOGTFO) - Android Developers Italia

Offline elfo83

  • Utente normale
  • ***
  • Post: 287
  • Respect: +23
    • Mostra profilo
  • Sistema operativo:
    Mac OS 10.8.2
Re:Oggetto HTTP response troppo grande
« Risposta #14 il: 25 Marzo 2013, 16:51:42 CET »
0
Ok grazie per le risposte, proverò a fare in questo modo. Nel caso volessi ogni volta che carico 500 JSONObject, rilasciare le risorse consumati dai 500 oggetti caricati in precedenza nella mia ListView, qual'è il migliore approccio secondo voi? Nel senso il miglio approccio per caricare i successivi 500 oggetti utilizzando al minimo le risorse..