Autore Topic: [facile] Aggiornare la UI da una Thread  (Letto 18088 volte)

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
[facile] Aggiornare la UI da una Thread
« il: 05 Marzo 2010, 21:01:30 CET »
+5
Livello di difficoltà: facile
Versione SDK utilizzata: 1.5
Link al file compresso del progetto eclipse: file in allegato

Prima della versione 1.5 di android, per poter eseguire aggiornamenti sull'interfaccia grafica durante l'esecuzione di una Thread, era necessario usare degli Handler dichiarati all interno di onCreate (UI thread) e usati all'interno delle classiche Thread java http://developer.android.com/guide/appendix/faq/commontasks.html#threading
oppure in alternativa usare il metodo runOnUiThread di Activity

Questo procedimento risulta a volte scomodo, e alla lunga porta ad una leggibilità del codice pressochè impossibile.
Per ovviare a questo problema, il framework mette a disposizione, a partire dalla versione 1.5, una comoda classe chiamata AsyncTask.
Questa classe oltre ad eseguire del codice in parallelo (Threading) mette a disposizione dei metodi per eseguire gli update alla nostra UI.

Vediamo un esempio del funzionamento di questa classe:

Creiamo un semplice 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="fill_parent">
        <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="press start to begin"
                android:id="@+id/loadingTv"
                android:inputType="textMultiLine" />

        <Button
                android:text="start"
                android:id="@+id/startButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
        </Button>
</LinearLayout>


Implementazione e commenti:
Codice (Java): [Seleziona]
public class Demo extends Activity {

        TextView loadingTv;

        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);

                loadingTv = (TextView) findViewById(R.id.loadingTv);
                Button start = (Button) findViewById(R.id.startButton);

                start.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View arg0) {
                                LoadingStuffTask task = new LoadingStuffTask();
                                task.execute("Process started!");
                                 
                                //la stringa verra passata al metodo doInBackground del nostro AsyncTask
                                //per controllare lo stato del task, possiamo chiamare task.getStatus()

                        }
                });

        }

        private class LoadingStuffTask extends AsyncTask<String, String, String> {
                // I tre tipi da dichiarare sono relativamente (in questo caso usiamo solo stringhe)
                // il parametro in entrata di doInBackground  (quando chiamiamo execute passiamo una stringa)
                // il paramentro in entrata di onProgressUpdate (dati che vogliamo mostrare all' utente -opzionale,potremmo usare variabili globali-)
                // il parametro in entrata di onPostExecure (una volta finito il task, potremmo voler mostrare un risultato all'utente)
                // nel caso non volessimo usare parametri di alcun genere tipizziamo con Void,Void,Void (passeremo poi dei null come parametri)

                @Override
                protected String doInBackground(String... params) {
                        publishProgress("\n" + params[0]);
                        // stringa passata a onProgressUpdate

                        Random rand = new Random();

                        publishProgress("Loading module 1 ...");

                        while (rand.nextInt(1000000) != 2) {
                        }
                        // "simuliamo" dei calcoli
                        publishProgress("Loading module 2 ...");
                        while (rand.nextInt(1000000) != 1) {
                        }

                        return "Finish !";
                        // stringa passata a onPostExecute
                }

                @Override
                protected void onProgressUpdate(String... values) {
                        loadingTv.append(values[0] + "\n");
                }

                @Override
                protected void onPostExecute(String result) {
                        loadingTv.append(result + "\n");
                }

        }

}

Bye  ;)



« Ultima modifica: 24 Marzo 2010, 13:52:13 CET da Qlimax »

Offline daniele

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: +1
    • Mostra profilo
Re:[facile] Aggiornare la UI da una Thread
« Risposta #1 il: 11 Maggio 2010, 11:32:41 CEST »
0
Molto utile e chiaro, grazie.

Offline daliz

  • Nuovo arrivato
  • *
  • Post: 13
  • I love Android!
  • Respect: 0
    • Mostra profilo
    • Datapixel
  • Dispositivo Android:
    Motorola Milestone
  • Play Store ID:
    Datapixel
  • Sistema operativo:
    Microsoft Windows 7
Re:[facile] Aggiornare la UI da una Thread
« Risposta #2 il: 20 Maggio 2010, 14:54:57 CEST »
0
Grazie Qlimax.

Solo una cosa. Ho letto in giro che questo metodo non si adatta bene ad essere usato come un thread continuo in background (ad esempio per un game loop). Sai mica il perchè?

Grazie ;)

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
Re:[facile] Aggiornare la UI da una Thread
« Risposta #3 il: 20 Maggio 2010, 15:41:50 CEST »
0
fonte?
Grazie Qlimax.

Solo una cosa. Ho letto in giro che questo metodo non si adatta bene ad essere usato come un thread continuo in background (ad esempio per un game loop). Sai mica il perchè?

Grazie ;)

Ciao daliz

Dove l'hai letto di preciso?
Forse una ragione è che AsyncTask al suo interno usa Thread, Handler e altri wrappper di Runnable/Thread per lavorare in background ed al tempo stesso comunicare con la UI.
Questo implica che una chiamata ad un metodo su asynctask, come ad esempio onProgressUpdate, si traduce in uno scambio di messaggi tra classi che sicuramente non favorisce la velocità (questione di millisecondi) del software ; specialmente su dispositivi mobili e per software che devono essere reattivi come i giochi. Questa è comunque una mia supposizione ed appunto vorrei verificare la fonte.

:)
« Ultima modifica: 20 Maggio 2010, 15:43:51 CEST da Qlimax »

Offline noodles

  • Utente junior
  • **
  • Post: 130
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Google Nexus One
  • Sistema operativo:
    Mac OS X Snow Leopard
Re:[facile] Aggiornare la UI da una Thread
« Risposta #4 il: 31 Maggio 2010, 20:38:15 CEST »
0
una curiosità, visto che io ho sempre utilizzato Thread ed Handler... c'è un particolare motivo per cui dovrei utilizzare un AsyncTask invece del normale approccio Thread+Handler? Intendo a parte la leggibilità del codice, che sicuramente ottiene un enorme beneficio. Ci sono pro e contro?

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3487
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
Re:[facile] Aggiornare la UI da una Thread
« Risposta #5 il: 31 Maggio 2010, 20:56:33 CEST »
0
una curiosità, visto che io ho sempre utilizzato Thread ed Handler... c'è un particolare motivo per cui dovrei utilizzare un AsyncTask invece del normale approccio Thread+Handler? Intendo a parte la leggibilità del codice, che sicuramente ottiene un enorme beneficio. Ci sono pro e contro?

Io utilizzo generalmente l'AsyncTask per popolare le listview, prima con Thread+Handler mi beccavo IllegalStateException a random... bah.

Offline DX89B

  • Utente junior
  • **
  • Post: 121
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire
  • Play Store ID:
    Daniele Bonadiman
  • Sistema operativo:
    Mac OS X 10.6
Re:[facile] Aggiornare la UI da una Thread
« Risposta #6 il: 08 Settembre 2010, 15:01:19 CEST »
0
Io utilizzo generalmente l'AsyncTask per popolare le listview, prima con Thread+Handler mi beccavo IllegalStateException a random... bah.
Sai xkè quando cerco di eseguire nuovamente l'AsyncTask(anche se precedentemente cancellato o terminato) per popolare una LV mi da

Codice: [Seleziona]
09-08 14:59:33.496: ERROR/AndroidRuntime(7063): java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)

devo ri dichiararlo?

Offline MarcoDuff

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 1073
  • Respect: +202
    • Google+
    • marcoduff
    • Mostra profilo
    • MarcoDuff's Blog
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    MarcoDuff
  • Sistema operativo:
    Windows 7
Re:[facile] Aggiornare la UI da una Thread
« Risposta #7 il: 08 Settembre 2010, 15:50:19 CEST »
0
Si, una classe Thread può partire (tramite metodo start()) una volta sola.

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3487
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
Re:[facile] Aggiornare la UI da una Thread
« Risposta #8 il: 08 Settembre 2010, 16:41:26 CEST »
0
Sai xkè quando cerco di eseguire nuovamente l'AsyncTask(anche se precedentemente cancellato o terminato) per popolare una LV mi da

Codice: [Seleziona]
09-08 14:59:33.496: ERROR/AndroidRuntime(7063): java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)

devo ri dichiararlo?

A quanto pare :)

Offline bovello

  • Nuovo arrivato
  • *
  • Post: 28
  • Respect: +2
    • Mostra profilo
    • Bovello
  • Dispositivo Android:
    San Francisco
  • Play Store ID:
    Bovello
  • Sistema operativo:
    Windows 7, Mac OS Snow Leopard
Re:[facile] Aggiornare la UI da una Thread
« Risposta #9 il: 12 Marzo 2011, 00:57:20 CET »
0
Ciao!
Scusate se riesumo questo post, ma sto riscontrando un problemino.
La mia applicazione chiama dei web services, per i quali utilizzo gli AsyncTask.
Al termine della chiamata vorrei popolare una listview (personalizzata con 4 textview per ciascun item della lista), tramite adapter(setListAdapter(new SimpleAdapter(.......)

Ho provato sia avendo la listview nella medesima activity, che in una activity embedded. Quanto provo a collegare l'adapter alla lista mi restituisce un errore:

E/AndroidRuntime(  273):        at java.lang.Thread.run(Thread.java:1096)
E/AndroidRuntime(  273): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Rileggendo la risposta di Ricky ho visto che lui utilizza gli AsyncTask proprio per popolare una listview. Avete qualche idea di come aggirare il problema?

Inoltre.... gli Asynctask non restituiscono nulla giusto? del resto cadrebbe il loro essere eseguiti in background se qualcuno rimanesse in attesa del risultato.

Grazie 1000,

 Marco

Offline DarKprince

  • Nuovo arrivato
  • *
  • Post: 24
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Saverio Guardato
  • Sistema operativo:
    Windows 7, Ubuntu 10.10
Re:[facile] Aggiornare la UI da una Thread
« Risposta #10 il: 12 Marzo 2011, 10:25:06 CET »
0
io aggiungerei anche quest'altro approccio, che può essere implementato direttamente da un normale Thread senza usare gli AsyncTask (posto direttamente il metodo che utilizzo):

Codice (Java): [Seleziona]
/**
         * if you wanna show a toast from a thread called by an activity you
         * must use the runOnUiThread
         * @param activity
         * @param infoMessage
         * @param messageLenght
         */

        public void showInfoThread(final Activity activity, final String infoMessage, final int messageLenght){        
                if(activity!=null){
                        activity.runOnUiThread(new Runnable() {
                                @Override              
                                public void run() {
                                        Toast.makeText(activity, infoMessage, messageLenght).show();
                                }
                                });            
                        }              
                }

Offline Nicola_D

  • 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:[facile] Aggiornare la UI da una Thread
« Risposta #11 il: 12 Marzo 2011, 10:40:51 CET »
0
io aggiungerei anche quest'altro approccio, che può essere implementato direttamente da un normale Thread senza usare gli AsyncTask (posto direttamente il metodo che utilizzo):

Codice (Java): [Seleziona]
/**
         * if you wanna show a toast from a thread called by an activity you
         * must use the runOnUiThread
         * @param activity
         * @param infoMessage
         * @param messageLenght
         */

        public void showInfoThread(final Activity activity, final String infoMessage, final int messageLenght){        
                if(activity!=null){
                        activity.runOnUiThread(new Runnable() {
                                @Override              
                                public void run() {
                                        Toast.makeText(activity, infoMessage, messageLenght).show();
                                }
                                });            
                        }              
                }

Questo però implica che gli passi l'activity al thread, e se mentre il thread lavora l'activity viene distrutta, credo venga lanciata un'eccezione (magari null pointer exception).
Io piuttosto userei gli handler
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 DarKprince

  • Nuovo arrivato
  • *
  • Post: 24
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Saverio Guardato
  • Sistema operativo:
    Windows 7, Ubuntu 10.10
Re:[facile] Aggiornare la UI da una Thread
« Risposta #12 il: 12 Marzo 2011, 10:47:53 CET »
0
Questo però implica che gli passi l'activity al thread, e se mentre il thread lavora l'activity viene distrutta, credo venga lanciata un'eccezione (magari null pointer exception).
Io piuttosto userei gli handler

il primo controllo
Codice (Java): [Seleziona]
if(activity!=null)serve proprio a questo =)

Offline Nicola_D

  • 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:[facile] Aggiornare la UI da una Thread
« Risposta #13 il: 12 Marzo 2011, 11:00:28 CET »
0
il primo controllo
Codice (Java): [Seleziona]
if(activity!=null)serve proprio a questo =)
che stupido! hai ragione!
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 bovello

  • Nuovo arrivato
  • *
  • Post: 28
  • Respect: +2
    • Mostra profilo
    • Bovello
  • Dispositivo Android:
    San Francisco
  • Play Store ID:
    Bovello
  • Sistema operativo:
    Windows 7, Mac OS Snow Leopard
Re:[facile] Aggiornare la UI da una Thread
« Risposta #14 il: 12 Marzo 2011, 12:20:18 CET »
0
Magari può essere utile a qualcun'altro...
Io ho risolto eseguendo il mio codice nel postExecute, che viene eseguito al completamento dell'asynctask.
Sarebbe interessante ad ogni modo capire se ci fossero altre soluzioni.
Ciao!