Autore Topic: AsyncTask dentro AsyncTask  (Letto 921 volte)

Offline Dav_android

  • Utente junior
  • **
  • Post: 78
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Tab2 10.1
  • Sistema operativo:
    Windows 8
AsyncTask dentro AsyncTask
« il: 23 Dicembre 2014, 11:52:23 CET »
0
Ragazzi vorrei capire quale dovrebbe essere la pratica giusta quando si scaricano dei dati dalla rete con delle immagini.
Vi spiego meglio. Nella mia app ho un activity che scarica delle news dal web per ogni news c'è anche un immagine, ho creato un asynctask che effettua la connessione e scaric ail json, l'immagine viene ricevuta come stringa del nome del file.

Con BitmapFactory le trasformo in immagini e poi mando i dati testuali e l'immagine a dun arrayadapter che mi crea la lista con view personalizzata.

Mi sono accorto che quando le informazioni e le immagini da scaricare sono molte l'app crasha quindi avevo pensato, e confermatemi se è giusto, di avviare un ulteriore asynctask all'interno del doInBackground del tack che si connette alla rete.
Il risultato che verranno ciclati i task di creazione bitmap per ogni oggetto della listview.

Il problema è che il task principale invia i dati all'array adapter, come faccio ad ottenere la bitmap generata come return dei task secondari ed inviarla correttamente all'arrayadapter?

Questo è il codice del doinbackground:

Codice (Java): [Seleziona]
@Override
        protected ArrayList<News> doInBackground(Void... params) {

            String s = null;
            InputStream is = null;
            StringBuffer sb = null;

            try {
                HttpClient httpclient = new DefaultHttpClient();
                HttpGet httpGet = new HttpGet("http://www.exsempe.it/index.php?ev=listaNews");
                HttpResponse response = httpclient.execute(httpGet);

                int responseCode = response.getStatusLine().getStatusCode();
                if (responseCode == 200) {
                    is = response.getEntity().getContent();

                    BufferedReader r = new BufferedReader(new InputStreamReader(is));
                    sb = new StringBuffer();
                    String result = null;
                    while((result = r.readLine()) != null) {
                        sb.append(result);
                    }
                    is.close();
                    s = sb.toString();
                } else {
                    s = "Errore di connessione";
                }
            } catch(Exception e) {
                e.printStackTrace();
            }

            rowItems = new ArrayList<News>();

            String json = s;
            JSONArray jsonArray;
            try {
                jsonArray = new JSONArray(json);
                //String[] arrayNews = new String[jsonArray.length()];
                for(int i = 0; i < jsonArray.length(); i++) {
                    String id_news = jsonArray.getJSONObject(i).getString("id_news");
                    String titolo_news_it = jsonArray.getJSONObject(i).getString("titolo_news_it");
                    String data_news = jsonArray.getJSONObject(i).getString("data_news");
                    String immagine_news = jsonArray.getJSONObject(i).getString("immagine_news");

                    new DownloadImageTask().execute(immagine_news);

                    String jsonImage = immagine_news;
                    JSONArray jsonArrayImage;
                    Bitmap bmp = null;
                    String image = "";
                    try {
                        jsonArrayImage = new JSONArray(jsonImage);
                        image = jsonArrayImage.getJSONObject(0).getString("image");

                        try {
                            URL url = new URL("http://www.exsempe.it/"+image);
                            bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
                        } catch (MalformedURLException e) {
                            e.printStackTrace();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    News item =  new News(id_news, titolo_news_it, data_news, bmp);
                    rowItems.add(item);
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }

            return (ArrayList<News>) rowItems;
        }

        @Override
        protected void onPostExecute(ArrayList<News> result) {
            progress.dismiss();
            newsAdapter = new NewsAdapter(NewsActivity.this, R.layout.row_news, rowItems);
            listView = (ListView) findViewById(R.id.listView);
            listView.setAdapter(newsAdapter);
            listView.setOnItemClickListener(new CliccaNewsListener());
        }

    }

In pratica l'oggetot news viene creato inviando al costruttore le stringhe più l'immagine bitmap e poi viene inserito nell'adapter. Forse è qui che sbaglio? Devo creare l'array adapter solo con le stringhe e poi per ogni row della lista genero un asynctask per crear ela bitmap?
« Ultima modifica: 23 Dicembre 2014, 12:08:44 CET da Dav_android »

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:AsyncTask dentro AsyncTask
« Risposta #1 il: 23 Dicembre 2014, 12:28:43 CET »
0
Come prima cosa per la connessione di rete usa una libreria già fatta (come la semplicissima android-asynchttp, oppure la più completa okhttp). Ormai se il 99,9% delle app usa una di queste librerie, una ragione c'è.

Poi una volta scaricati dati, puoi avviare i tuoi thread/task di postprocessing.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline Dav_android

  • Utente junior
  • **
  • Post: 78
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Tab2 10.1
  • Sistema operativo:
    Windows 8
Re:AsyncTask dentro AsyncTask
« Risposta #2 il: 23 Dicembre 2014, 13:18:19 CET »
0
Non vorrei usare librerie perchè voglio imparare bene e capire cosa faccio in ogni riga di codice, poi magari integrerò librerie esterne.


Quindi secondo te dove dovrei avviare il task che genera l'immagine bitmap nella classe dell'array adapter?

Post unito: 23 Dicembre 2014, 14:20:47 CET
Forse ho capito perchè l'app si arresta. in pratica il dowload degli elementi avviene ogni volta che vado a finire nell'activity che contiene la listview, la cosa succede solo se ho molti elementi in lista, avviene un outofmemory. Da che cosa dipende e come si evita questo errore?
« Ultima modifica: 23 Dicembre 2014, 14:20:47 CET da Dav_android, Reason: Merged DoublePost »

Offline wlf

  • Utente normale
  • ***
  • Post: 359
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:AsyncTask dentro AsyncTask
« Risposta #3 il: 23 Dicembre 2014, 15:28:17 CET »
0
Io partire spostando dalla onPostExecute() il codice relativo alla listview ed adapter mettendolo nella onPreExecute() se non nella onCreate() dell'activity che contiene la listview.

Codice: [Seleziona]
            listView = (ListView) findViewById(R.id.listView);
            newsAdapter = new NewsAdapter(NewsActivity.this, R.layout.row_news, rowItems);
            listView.setAdapter(newsAdapter);
            listView.setOnItemClickListener(new CliccaNewsListener());

Nell'AsynkTask per ogni riga aggiunta  rowItems.add(item) chiamerei una publishProgress() ed in questa, la onProgressUpdate()  chiamerei una notifyDataSetChanged(). In questo modo ti parte subito la listview e man mano che viene fatto il download delle immagini ti compaiono le righe ...
Se non si risolve il problema dell'outofmemory presumo che dovrai spostare il download delle immagini sull'adapter, in modo che ti vengano downloadate man mano che visualizzi le righe, non come fai ora che fai il download completo caricando tutto su un ArrayList<News>.
Quante sono le righe e che dimensioni hanno mediamente le immmagini?

Offline Dav_android

  • Utente junior
  • **
  • Post: 78
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Tab2 10.1
  • Sistema operativo:
    Windows 8
Re:AsyncTask dentro AsyncTask
« Risposta #4 il: 24 Dicembre 2014, 08:47:55 CET »
0
Le righe nel tempo potrebbero diventare anche centinaia, non è un numero finito. Le immagini possono variare tra i 30 e i 100 kb.


Mi faresti un esempio per far uscire le righe man mano al progress?

Offline wlf

  • Utente normale
  • ***
  • Post: 359
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:AsyncTask dentro AsyncTask
« Risposta #5 il: 24 Dicembre 2014, 10:40:59 CET »
0
Come ho già scritto ti basta aggiungere la publishProgress dietro all'aggiunta di ogni nuova riga:

Codice: [Seleziona]
rowItems.add(item);
publishProgress();

Sotto alla doInBackground() dentro l'asyncTask metti la onProgressUpdate() con dentro una riga con la notifyDataSetChanged():

Codice: [Seleziona]
    @Override
    protected void onProgressUpdate(Void... v) {
        newsAdapter.notifyDataSetChanged();
    }

Offline Dav_android

  • Utente junior
  • **
  • Post: 78
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Tab2 10.1
  • Sistema operativo:
    Windows 8
Re:AsyncTask dentro AsyncTask
« Risposta #6 il: 28 Dicembre 2014, 09:18:13 CET »
0
Ho provato a seguire il tuo consiglio per pubblicare una riga per volta, il problema è che se sposto il seguente codice:

Codice (Java): [Seleziona]
newsAdapter = new NewsAdapter(NewsActivity.this, R.layout.row_news, rowItems);
listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(newsAdapter);
listView.setOnItemClickListener(new CliccaNewsListener());

nell'onCreate o nell'onPreExecute l'app si arresta.
La stessa cosa succede anche se lascio quelle righe nell'onPostExecute e imposto solo le due righe di codice: publishProgress(); sotto rowItems.add(item); e  newsAdapter.notifyDataSetChanged(); nel metodo onProgressUpdate.

Ragazzi vi posto l'intero file, datemi qualche consiglio davvero ci sono bloccato da settimane e non riesco ad ottimizzare questa funzionalità, penso che sia alla base per scaricare dati dalla rete e quindi se non riesco a sistemare questa cosa non posso andare avanti.

Tralascio il codice del menù e dell'evento onclick sugli elementi della lista il resto metto tutto dall'oncreate al doinbackground.

Codice (Java): [Seleziona]
public class NewsActivity extends ActionBarActivity {

    boolean connection = false;
    ProgressDialog progress = null;
    NewsAdapter newsAdapter;
    ListView listView;
    List<News> rowItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news);

        progress = new ProgressDialog(NewsActivity.this);
        progress.setMessage("Progress...");
        progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progress.setCancelable(false);

        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            connection = true;
            new BackgroundTask().execute();
        } else {
            connection = false;
            Toast.makeText(NewsActivity.this, "Dispositivo non connesso! Riprova.", Toast.LENGTH_SHORT).show();
        }

    }

    private class BackgroundTask extends AsyncTask<Void, Integer, ArrayList<News>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progress.show();
        }

        @Override
        protected ArrayList<News> doInBackground(Void... params) {

            String s = null;
            InputStream is = null;
            StringBuffer sb = null;

            try {
                HttpClient httpclient = new DefaultHttpClient();
                HttpGet httpGet = new HttpGet("http://www.miosito.it");
                HttpResponse response = httpclient.execute(httpGet);

                int responseCode = response.getStatusLine().getStatusCode();
                if (responseCode == 200) {
                    is = response.getEntity().getContent();

                    BufferedReader r = new BufferedReader(new InputStreamReader(is));
                    sb = new StringBuffer();
                    String result = null;
                    while((result = r.readLine()) != null) {
                        sb.append(result);
                    }
                    is.close();
                    s = sb.toString();
                } else {
                    s = "Errore di connessione";
                }
            } catch(Exception e) {
                e.printStackTrace();
            }

            rowItems = new ArrayList<News>();

            String json = s;
            JSONArray jsonArray;
            try {
                jsonArray = new JSONArray(json);
                for(int i = 0; i < jsonArray.length(); i++) {
                    String id_news = jsonArray.getJSONObject(i).getString("id_news");
                    String titolo_news_it = jsonArray.getJSONObject(i).getString("titolo_news_it");
                    String data_news = jsonArray.getJSONObject(i).getString("data_news");
                    String immagine_news = jsonArray.getJSONObject(i).getString("immagine_news");

                    String jsonImage = immagine_news;
                    JSONArray jsonArrayImage;
                    Bitmap bmp = null;
                    String image = "";
                    try {
                        jsonArrayImage = new JSONArray(jsonImage);
                        image = jsonArrayImage.getJSONObject(0).getString("image");

                        try {
                            URL url = new URL("http://www.miosito.it/img/"+image);
                            bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
                        } catch (MalformedURLException e) {
                            e.printStackTrace();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    News item =  new News(id_news, titolo_news_it, data_news, image, bmp);
                    rowItems.add(item);
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }

            return (ArrayList<News>) rowItems;
        }

        /*
                protected void onProgressUpdate(Integer... progress) {

            }
        */


        @Override
        protected void onPostExecute(ArrayList<News> result) {
            progress.dismiss();
            newsAdapter = new NewsAdapter(NewsActivity.this, R.layout.row_news, rowItems);
            listView = (ListView) findViewById(R.id.listView);
            listView.setAdapter(newsAdapter);
            listView.setOnItemClickListener(new CliccaNewsListener());
        }

    }
}

Non solo vorrei che le righe venissero pubblicate una per volta durate il progresso, ma se fosse necessario per migliorare le prestazioni vorrei anche poter caricare le immagini su task separati. In pratica ora l'oggetto News ha 3 tringhe e un'immagine, ma il posso anche togliere l'immagine ed avere la stringa con la url soltanto e poi eseguire dei task per ogni riga della lista che creino la bitmap da inserire nell'imageview.

Resta ancor aun altro problema, l'attuale file come ve l'ho postato funziona, carica tutti i contenuti in un arrai li aggiunge alal lista e poi pubblica la lista, solo che come dicevo precedentemente quando vado in questa activity l'app scarica tutto il contenuto se passo in un'altra acrivity dell'app e poi ritorno in quella della lista news l'app si arresta con errore outof memory forse perchè ogni volta che si esce dalla'activity si deve liberare la memoria? Se si come si fa?
« Ultima modifica: 28 Dicembre 2014, 09:27:58 CET da Dav_android »

Offline wlf

  • Utente normale
  • ***
  • Post: 359
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:AsyncTask dentro AsyncTask
« Risposta #7 il: 29 Dicembre 2014, 09:24:31 CET »
0
Ho provato a seguire il tuo consiglio per pubblicare una riga per volta, il problema è che se sposto il seguente codice:

Codice (Java): [Seleziona]
newsAdapter = new NewsAdapter(NewsActivity.this, R.layout.row_news, rowItems);
listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(newsAdapter);
listView.setOnItemClickListener(new CliccaNewsListener());

nell'onCreate o nell'onPreExecute l'app si arresta.

Che errore ti da spostando tutto nella onCreate? Perché sei in una "extends ActionBarActivity" invece che una "extends Activity"?
Se non ti andasse in OutOfMemory nel caricamento della List rowItems probabilmente poi avresti lo stesso identico errore nella onPostExecute quando eseguiresti le stesse righe di codice. :)
Se togli il caricamento delle immagini per alleggerire il carico della memoria potresti già verificarlo ...


« Ultima modifica: 29 Dicembre 2014, 09:30:09 CET da wlf »

Offline Dav_android

  • Utente junior
  • **
  • Post: 78
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Tab2 10.1
  • Sistema operativo:
    Windows 8
Re:AsyncTask dentro AsyncTask
« Risposta #8 il: 29 Dicembre 2014, 09:35:34 CET »
0
La ActionBarActivity è l'activity con actionbar data di default con la libreria v7 per le versioni più vecchie di android quindi non dovrebbe influenzare, ho provato anche a togliere le immagini quindi non è un problema di memoria ora si arresta solo spostando quel codice. sto provando a riprogettare l'activity per alleggerire un po' il codice. Dopo te la posto.

Offline Dav_android

  • Utente junior
  • **
  • Post: 78
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Tab2 10.1
  • Sistema operativo:
    Windows 8
Re:AsyncTask dentro AsyncTask
« Risposta #9 il: 31 Dicembre 2014, 10:18:36 CET »
0
Ho rifatto il tutto in modo più pulito e ho seguito il tuo consiglio di inserire sotto all' add della lista publishProgress(); e nel metodo onProgressUpdate la riga di codice: newsAdapter.notifyDataSetChanged();

Ho letto nella documentazione ufficiale e notifyDataSetChanged() è un metodo dell'arrayAdapter per refreschare la lista quando ci sono dei cambiamenti.

Ho notato però che se elimino publishProgress(); l'app funziona ugualmente e la stessa cosa succede se elimino notifyDataSetChanged() e lascio publishProgress().

Qual'è la differenza e perchè inserire entrambe le righe di codice, è una questione di prestazioni?