Autore Topic: riavvio Activity, ProgressDialog e Thread  (Letto 1729 volte)

Offline gigi

  • Nuovo arrivato
  • *
  • Post: 42
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Gentoo
riavvio Activity, ProgressDialog e Thread
« il: 19 Dicembre 2010, 17:46:39 CET »
0
hei!

Man mano che entro in Android mi viene da pensare che forse c'è qualcosa che poteva essere pensato meglio. Comunque... è presto per me per dare un giudizio. Ma vengo al problema.

Situazione (penso) tipica: Activity con bottone, bottone che scatena il lancio di un Thread per una attività asincrona unito alla comparsa di un bel ProgressDialog, che verrà tolto al termine dell'esecuzione del thread; fin qui tutto ok.

Giro il telefono, lo metto orizzontale: Android distrugge e riavvia l'activity, che si presenta correttamente, ben impaginata e con anche i widget nello stato in cui erano visto che li avevo tenuti dentro Application. Manca però il ProgressDialog (che dovrebbe comunque esserci, visto che l'attività asincrona non è terminata).
Ad un certo punto termina il thread, la logica susseguente fa per togliere il ProgressDialog già morto e BUM!

Qual è un buon modo per affrontare questa situazione?




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:riavvio Activity, ProgressDialog e Thread
« Risposta #1 il: 19 Dicembre 2010, 17:52:58 CET »
+2
Questo dovrebbe essere un esempio di come gestire il tutto.

https://github.com/commonsguy/cw-android/tree/master/Rotation/RotationAsync/

Se ho capito bene l'AsyncTask non è più inner e quando avviene la rotazione, si aggancia alla nuova activity.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline gigi

  • Nuovo arrivato
  • *
  • Post: 42
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Gentoo
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #2 il: 19 Dicembre 2010, 18:04:28 CET »
0
molto interessante. Non ricorre però ad un ProgressDialog, ma solo ad una ProgressBar. Ora leggo meglio e cerco di capire se si riesce anche con il ProgressDialog.

grazie!

Offline gigi

  • Nuovo arrivato
  • *
  • Post: 42
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    LG-P500
  • Sistema operativo:
    Gentoo
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #3 il: 19 Dicembre 2010, 21:03:51 CET »
0
Ho studiato un po' la faccenda e a quanto pare non è esattamente un problema legato ai Thread ma piuttosto ai Dialog. Frugando un po' in rete ho trovato parecchia gente che si lamenta di questa cosa (in effetti sarebbe opportuno che fosse Android a gestirlo) e diverse soluzioni. In fondo a questa pagina ce n'è una che mi pare buona:

android - How to handle screen orientation change when progress dialog and background thread active? - Stack Overflow

ma voi come fate per gestire il caso del Dialog aperto durante il riavvio dell'activity?

Offline zipgenius

  • Utente junior
  • **
  • Post: 80
  • Respect: +17
    • matteoriso
    • Mostra profilo
    • ZipGenius
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Wininizio.it Software
  • Sistema operativo:
    Windows 7
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #4 il: 06 Gennaio 2011, 15:07:46 CET »
0
Ho risolto!!! Datemi il tempo di sistemare un po' il codice e pubblico tutto qui ;)

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #5 il: 06 Gennaio 2011, 15:17:37 CET »
0
Ho risolto!!! Datemi il tempo di sistemare un po' il codice e pubblico tutto qui ;)

Ottimo :D
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline zipgenius

  • Utente junior
  • **
  • Post: 80
  • Respect: +17
    • matteoriso
    • Mostra profilo
    • ZipGenius
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Wininizio.it Software
  • Sistema operativo:
    Windows 7
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #6 il: 06 Gennaio 2011, 16:10:30 CET »
+1
Dunque, sebbene rimangano alcuni problemi relativi all'annullamento dell'AsyncTask (che potremmo provare a risolvere insieme), sono riusci a non far interrompere l'esecuzione di un AsyncTask legato ad un ProgressDialog dopo la rotazione dello schermo.

La base di partenza è il demo ProgressBars3 contenuto nel demo "ApiDemos" distribuito con l'SDK. Tale demo mostra solo come creare ProgressDialogs indeterminati ma, tuttavia, alla rotazione dello schermo tali dialoghi si riposizionavano e restavano attivi.
Il problema era far ruotare un ProgressDialog con barra di progessione. Il trucco sta nel creare il ProgressDialog come static, al di fuori di qualsiasi altra area del codice, e nel creare un "ponte" di collegamento tra il dialogo creato e l'AsyncTask che lo deve aggiornare.

Questo è l'XML del layout:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content">

    <Button android:id="@+id/showIndeterminate"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" android:text="Progressdialog, titolo e bottone Annulla"/>

    <Button android:id="@+id/showIndeterminateNoTitle"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" android:text="Progressdialog semplice"/>

</LinearLayout>

e questo è il codice:
Codice (Java): [Seleziona]
package com.wisoft.pddemo;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class ProgressBar3 extends Activity {

    static ProgressDialog myDialog; // Questo è il nostro "ponte"

    // Vogliamo creare due dialoghi diversi, quindi due ID diversi
    private static final int DIALOG1_KEY = 0;    
    private static final int DIALOG2_KEY = 1;

    // Scriviamo il codice per far creare il dialogo corrispondente
    // in funzione dell'ID inviato alla pressione di ciascun tasto
    protected Dialog onCreateDialog(int id) {
        ProgressDialog dialog = new ProgressDialog(this);
        switch (id) {
            case DIALOG1_KEY: {
                dialog.setTitle("Attendere prego..."); // Inseriamo un titolo
                dialog.setMessage("Caricamento in corso."); // Scriviamo un messaggio
                dialog.setIndeterminate(false); // Vogliamo una progressbar definita
                dialog.setMax(44); // Indichiamo il valore massimo della progressbar
                dialog.setProgress(0); // Per sicurezza...
                dialog.setCancelable(true); // Il ProgressDialog sarà cancellabile

                // Creiamo il tasto "Annulla" e il relativo OnClickListener
                dialog.setButton("Annulla", new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface arg0, int arg1) {
                                                return;                                        
                                        }
                                });
               
                // Impostiamo lo stile della progressbar
                dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
               
                // Colleghiamo il dialog creato a myDialog, così da poterlo gestire nell'AsyncTask
                myDialog = dialog;
                return dialog;
            }
           
            case DIALOG2_KEY: {
               
                // Creiamo il secondo tipo di dialogo, senza titolo nè tasto "Annulla"
                dialog.setMessage("Caricamento in corso.");
                dialog.setIndeterminate(false);
                dialog.setCancelable(false);
                dialog.setMax(44);
                dialog.setProgress(0); // Per sicurezza...
                dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
               
             // Colleghiamo anche questo dialog alternativo a myDialog, così da poterlo gestire nell'AsyncTask
                myDialog = dialog;
                return dialog;
            }
        }
        return null;
    }
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button button = (Button) findViewById(R.id.showIndeterminate);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {                
                showDialog(DIALOG1_KEY); // Vogliamo usare il dialog con titolo e bottone "Annulla"
                MyTask task = new MyTask(); // Creiamo e avviamo l'AsyncTask
                                task.execute("", "", "");       // che riempirà la progressbar - gli argomenti sono vuoti perché non ci servono in questo esempio                            
            }
        });

        button = (Button) findViewById(R.id.showIndeterminateNoTitle);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {            
                showDialog(DIALOG2_KEY);  // Come sopra, ma qui avremo il progressdialog senza titolo e non cancellabile
                MyTask task = new MyTask();
                                task.execute("", "", "");
            }
        });
    }
   
    // Questo è l'AsyncTask che simula l'esecuzione di un'operazione
    // per la quale vogliamo mostrare la progressione
    static class MyTask extends AsyncTask<String, Integer, String> {
        @Override
                protected String doInBackground(String... params) {
                        for (int i=0; i<45; i++) {
                                try {
                                        Thread.sleep(1000);
                                        publishProgress(i); // inviamo il valore all'evento onProgressUpdate...
                                } catch (InterruptedException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                }
                        }
                       
                        return null;                  
           
                }
                               
                protected void onProgressUpdate(Integer... values) {                             
                if (isCancelled())
                        myDialog.dismiss();
                        myDialog.setProgress(values[0]); // ... e aggiorniamo la progressbar                           
            }

            @Override
            protected void onPostExecute(String result) {
                myDialog.dismiss();            
             }         
    }    
}

Il codice è commentato.
L'unico problema che nella fretta non ho avuto modo di studiare e risolvere è che non si riesce a fermare l'AsyncTask. Mi spiego: se clicco il tasto "Annulla" il ProgressDialog sparisce ma se clicco ancora il bottone 1 per far ripartire l'operazione, mi riappare il ProgressDialog fermo al punto in cui avevvo cliccato "Annulla", quindi la ProgressBar comincia a comportarsi stranamente, come se volesse ripartire da zero e contemporaneamente proseguire il conteggio precedente.
Ad ogni modo, ruotando il dispositivo (o l'emulatore con CTRL+F11) il ProgressDialog si riposiziona come richiesto e non si interrompe.
Resta solo da risolvere il problema esposto prima: fermare l'AsyncTask quando si annulla l'operazione.

Offline zipgenius

  • Utente junior
  • **
  • Post: 80
  • Respect: +17
    • matteoriso
    • Mostra profilo
    • ZipGenius
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Wininizio.it Software
  • Sistema operativo:
    Windows 7
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #7 il: 07 Gennaio 2011, 13:59:19 CET »
+3
Ho rimaneggiato il codice postato in precedenza e sono riuscito a risolvere definitivamente anche il problema esposto (cioè: annullare il ProgressDialog non fa interrompere la progressione in background).

Scartabellando ancora nel codice di ApiDemos - una fonte preziosissima di spunti - ho trovato una soluzione radicalmente diversa e alternativa: anziché usare AsyncTask per gestire un'operazione lunga per la quale vogliamo l'indicazione di avanzamento in un ProgressDialog, torniamo ad usare la cara vecchia accoppiata Thread+Handler.
Nel demo ApiDemos > App > Dialogs abbiamo tutto il codice che ci serve per creare un ProgressDialog, renderlo definito impostando un valore massimo, aggiungere uno o più bottoni (nel nostro esempio aggiungeremo "Annulla" per interrompere l'avanzamento) e mostrare il ProgressDialog quando clicchiamo sul bottone relativo nel Layout.
Il codice di partenza, però, soffre del ben noto problema che si presente se ruotiamo il dispositivo mentre è in esecuzione un ProgressDialog con progressbar: il conteggio si ferma e si può persino andare incontro ad un crash dell'applicazione. Questo accade perché alla rotazione del dispositivo, l'Activity viene distrutta e ricreata da zero in modalità diversa (da portrait a landscape o viceversa) e, con essa, viene distrutto tutto il suo contenuto. In queste condizioni, il ProgressDialog non sa più qual è l'activity di riferimento, cioé quella che l'ha creato e lanciato, quindi finisce in una specie di "limbo" che potrebbe generare esiti inaspettati.
Il trucco in questo caso è semplicissimo e l'ho illustrato quando ho postato il codice precedente: il ProgressDialog deve essere dichiarato come static e, in questo nuovo esempio, anche l'Handler dovrà seguire la stessa sorte.

Non ripubblico qui l'XML dell'activity perché è sempre quello del post precedente, mentre il codice Java è profondamente cambiato ed è, in buona sostanza, lo stesso codice trovato in ApiDemos, con l'eccezione delle dichiarazioni appena citate. Il codice è ampiamente commentato.
Codice (Java): [Seleziona]
package com.wisoft.pddemo;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;

public class ProgressBar3 extends Activity {

   
    /** Le seguenti dichiarazioni sono quelle che consentono al
     *   ProgressDialog di "ruotare" quando ruotiamo il dispositivo
     *   senza far sì che questo mandi in crash la nostra applicazione
     */

    private static ProgressDialog dialog; // Questo è il nostro ProgressDialog
    private static int mProgress; // Variabile contenente il valore della progressione
    private static Handler mProgressHandler; // Handler per il thread che aggiornerà la progressbar
   
     
    // Vogliamo creare due dialoghi diversi, quindi due ID diversi
    private static final int DIALOG1_KEY = 0;    
    private static final int DIALOG2_KEY = 1;
   
       
    // Scriviamo il codice per far creare il dialogo corrispondente
    // in funzione dell'ID inviato alla pressione di ciascun tasto
    protected Dialog onCreateDialog(int id) {
       
        switch (id) {
            case DIALOG1_KEY: {
                dialog = new ProgressDialog(this);
                dialog.setTitle("Attendere prego..."); // Inseriamo un titolo
                dialog.setMessage("Caricamento in corso."); // Scriviamo un messaggio
                dialog.setIndeterminate(false); // Vogliamo una progressbar definita
                dialog.setMax(44); // Indichiamo il valore massimo della progressbar
                dialog.setProgress(0); // Per sicurezza...
               
                // Impostiamo lo stile della progressbar
                dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);                
                dialog.setCancelable(true); // Il ProgressDialog sarà cancellabile
               
                // Creiamo il tasto "Annulla"
                dialog.setButton("Annulla", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        /* Codice da eseguire alla pressione di Annulla */
                    }
                });
                return dialog;
            }
           
            case DIALOG2_KEY: {
               
                // Creiamo il secondo tipo di dialogo, senza titolo nè tasto "Annulla"
                dialog.setMessage("Caricamento in corso.");
                dialog.setIndeterminate(false);
                dialog.setCancelable(false);
                dialog.setMax(44);
                dialog.setProgress(0); // Per sicurezza...
                dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
             
                return dialog;
            }
        }
        return null;
    }
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button button = (Button) findViewById(R.id.showIndeterminate);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {                
                showDialog(DIALOG1_KEY); // Vogliamo usare il dialog con titolo e bottone "Annulla"
                mProgress = 0; // Azzeriamo la progressbar
                dialog.setProgress(0); // ... e comunichiamoglielo!
                mProgressHandler.sendEmptyMessage(0); // Quindi inviamo un messaggio vuoto (iniziale) all'Handler
            }
        });
     
       
        button = (Button) findViewById(R.id.showIndeterminateNoTitle);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {            
                showDialog(DIALOG2_KEY);  // Come sopra, ma qui avremo il progressdialog senza titolo e non cancellabile
            }
        });
       
        mProgressHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (mProgress >= 44) {
                    dialog.dismiss();
                } else {
                    mProgress++;
                    dialog.incrementProgressBy(1);
                    mProgressHandler.sendEmptyMessageDelayed(0, 1000);
                }
            }
        };
    }
}

Avviamo l'applicazione e attendiamo che essa inizia a mostrare l'avanzamento. Verifichiamo che la progressione continui anche quando si ruota il dispositivo e che cliccando "Annulla", una nuova pressione del primo bottone faccia ripartire il conteggio da zero.

Spero di essere stato utile a tutti :)
« Ultima modifica: 07 Gennaio 2011, 15:26:28 CET da zipgenius »

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #8 il: 07 Gennaio 2011, 18:04:13 CET »
0
Ciao zipgenius, grazie per aver condiviso queste utili informazioni :)

Io in questo periodo sono parecchio preso (ho una scadenza urgente a breve) e temo di non riuscire a trovare nemmeno il tempo per provare la tua soluzione :( Comunque me la segno perché mi tornerà sicuramente utile (mi pare che mi serva proprio in Libretto Universitario!).

Un suggerimento: dato che il codice è ben documentato, onde evitare che si perda di vista, che ne diresti di fare un tutorial a riguardo? Alla fine mi pare manchi solo uno screenshot e l'archivio compresso con i sorgenti per soddisfare i requisiti ;)

O se preferisci non pubblicare il tutorial, potresti considerare almeno la creazione di uno snippet ;)

Un saluto e grazie ancora
« Ultima modifica: 08 Gennaio 2011, 02:52:51 CET da JD »
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline zipgenius

  • Utente junior
  • **
  • Post: 80
  • Respect: +17
    • matteoriso
    • Mostra profilo
    • ZipGenius
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Wininizio.it Software
  • Sistema operativo:
    Windows 7
Re:riavvio Activity, ProgressDialog e Thread
« Risposta #9 il: 07 Gennaio 2011, 23:59:54 CET »
0
Facciamo così: sto applicando questa soluzione anche al mio primo prodotto per Android, quindi sicuramente ci sarà ancora qualcosa da aggiungere per spiegare meglio il tutto (per esempio: in quale area del codice bisogna scrivere l'operazione che si vuole far eseguire scandita dal progressdialog). Appena ritengo di non dover più modificare alcunché, scrivo il tutorial e aggiungo lo zip con il codice :)