Dato che durante l'esecuzione di un Long Task la UI (user interface) risulta, per così dire, congelata in attesa che l'operazione finisca, non sarà possibile mostrare la progress bar o l'immagine animata che faccia intuire all'utente che vi è in corso un'operazione.
Per ovviare a tale inconveniente occorre eseguire il Long Task all'interno di un Thread. In questo modo la UI risulterà indipendente dal Long Task in esecuzione all'interno del Thread, e sarà pertanto possibile mostrare messaggi e progress bar. Tuttavia la gestione per mostrare e rimuovere il messaggio con la progress bar, nonchè la notifica dell'esito dell'operazione richiede un lavoro di una certa complessità, in quanto si tratta di mettere in comunicazione due Thread che viaggiano indipendentemente l'uno dall'altro. Tra le API che Android mette a disposizione vi è AsyncTask, al quale si possono passare parametri di input, gestisce il progress dell'operazione in corso ed è un grado di effettuare una callback al Thread chiamante per notificare la risposta del Long Task.
In questo post illustrerò con un semplice esempio come si può utilizzare AsyncTask.
Per prima cosa occorre creare un progetto Android, che chiameremo LongTask, col seguente layout:
che si ottiene con il seguente main.xml:
L'activity principale è la classe LongTaskActivity, mentre il servizio la classe LongService, i cui sorgenti sono riportati di seguito.
file: LongTaskActivity.java:
package it.rino; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class LongTaskActivity extends Activity { public ProgressDialog pleaseWaitDialog = null; public void openPleaseWaitDialog() { if(pleaseWaitDialog == null) { pleaseWaitDialog = new ProgressDialog(LongTaskActivity.this); pleaseWaitDialog.setMessage("Please wait..."); pleaseWaitDialog.setIndeterminate(true); pleaseWaitDialog.setCancelable(true); pleaseWaitDialog.show(); } } public void closePleaseWaitDialog(){ if(pleaseWaitDialog != null) { if(pleaseWaitDialog.isShowing()) pleaseWaitDialog.hide(); pleaseWaitDialog.dismiss(); pleaseWaitDialog = null; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonReverseText = (Button)findViewById(R.id.buttonReverseText); buttonReverseText.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { EditText editTextInput = (EditText)findViewById(R.id.editTextInput); String input = editTextInput.getText().toString(); (new LongService(LongTaskActivity.this, "handleLongServiceResponse")).execute(input); } }); } public void handleLongServiceResponse(String output) { EditText editTextInput = (EditText)findViewById(R.id.editTextInput); editTextInput.setText(output); } }
file: LongService.java
package it.rino; import java.lang.reflect.Method; import android.os.AsyncTask; // Params, Progress, Result public class LongService extends AsyncTask{ private LongTaskActivity activity; private String callbackMethod; @Override protected void onPreExecute() { super.onPreExecute(); activity.openPleaseWaitDialog(); } public LongService(LongTaskActivity activity, String callbackMethod) { this.activity = activity; this.callbackMethod = callbackMethod; } @Override protected String doInBackground(String... input) { String in = input[0]; try { Thread.sleep(5000); } catch (InterruptedException e) { } String out = ""; int size = in.length(); for (int i = 0 ; i < size; i++) { out += in.charAt(size-1-i); } return out; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); activity.closePleaseWaitDialog(); // callback al metodo che gestisce la risposta del servizio try { Class classe = activity.getClass(); Class[] argTypes = new Class[] { result.getClass() }; Method metodo = classe.getDeclaredMethod(callbackMethod, argTypes); metodo.invoke(activity, result); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onCancelled() { super.onCancelled(); activity.closePleaseWaitDialog(); } }
Nell'Activity principale sull'evento onClick() si esegue il Long Task passando come Stringa di input, al metodo execute, il contenuto dell'InputText. Viene anche passato il nome del metodo che dovrà essere chiamato dalla callback per restituire la risposta all'activity chiamante. Tale metodo dovrà essere pubblico.
(new LongService(LongTaskActivity.this, "handleLongServiceResponse")).execute(input);
Il metodo pubblico handleLongServiceResponse, come detto sopra gestisce la risposta del Long Task rimpiazzando il testo iniziale con quello tornato da LongService all'interno dell'InputText.
public void handleLongServiceResponse(String output) { EditText editTextInput = (EditText)findViewById(R.id.editTextInput); editTextInput.setText(output); }All'interno dell'activity principale sono presenti i metodi per far comparire e rimuovere il messaggio utente presente nella ProgressDialog:
public ProgressDialog pleaseWaitDialog = null; public void openPleaseWaitDialog() { if(pleaseWaitDialog == null) { pleaseWaitDialog = new ProgressDialog(LongTaskActivity.this); pleaseWaitDialog.setMessage("Please wait..."); pleaseWaitDialog.setIndeterminate(true); pleaseWaitDialog.setCancelable(true); pleaseWaitDialog.show(); } } public void closePleaseWaitDialog(){ if(pleaseWaitDialog != null) { if(pleaseWaitDialog.isShowing()) pleaseWaitDialog.hide(); pleaseWaitDialog.dismiss(); pleaseWaitDialog = null; } }
Vediamo come viene implementato AsyncTask all'interno di LongService.
Per prima cosa occorre che LongService estenda AsyncTask e dichiari parametri di input, la gestione del Progress e il parametro di Result. Questo si ottiene nel modo seguente:
// Params, Progress, Result public class LongService extends AsyncTask{
In questo caso si è dichiarato di tipo String l'elenco dei parametri di input, per cui il metodo doInBackground() avrà la seguente forma:
@Override protected String doInBackground(String... input) { String in = input[0]; try { Thread.sleep(5000); } catch (InterruptedException e) { } String out = ""; int size = in.length(); for (int i = 0 ; i < size; i++) { out += in.charAt(size-1-i); } return out; }Dove il parametro di input utilizzato è il primo presente nell'array, si simula il Long Task con una pausa di 5 secondi, si inverte il testo passato in input e lo si ritorna. A questo punto AsyncTask passa la stringa di risposta al metodo onPostExecute() che si occupa di effettuare la callback al chiamante invocando dinamicamente il metodo dichiarato sull'activity pricipale:
@Override protected void onPostExecute(String result) { super.onPostExecute(result); activity.closePleaseWaitDialog(); // callback al metodo che gestisce la risposta del servizio try { Class classe = activity.getClass(); Class[] argTypes = new Class[] { result.getClass() }; Method metodo = classe.getDeclaredMethod(callbackMethod, argTypes); metodo.invoke(activity, result); } catch (Exception e) { e.printStackTrace(); } }
Come si può notare la prima operazione fatta dal metodo onPostExecute() è quella di chiudere la ProgressDialog che era stata aperta dal metodo onPreExecute() prima di richiamare doInBackground(), come si può verificare di seguito:
@Override protected void onPreExecute() { super.onPreExecute(); activity.openPleaseWaitDialog(); }
A questo punto non resta che mostrare come si presenta il terminale subito dopo aver premuto il bottone "Reverse Text":
E come si presenta terminata l'esecuzione del Long Task e rimossa la ProgressDialog:
Se si cambia lo stile della progressbar in STYLE_HORIZONTAL è possibile mostrare il progress dell'operazione gestendo l'avanzamento della progressbar all'interno del metodo onProgressUpdate(Integer... values) della classe LongService.
Per fare ciò occorre aggiungere il metodo pleaseWaitDialogUpdate(int value) ed effettuare alcune modifiche al metodo openPleaseEaitDialog() nella classe LongTaskActivity:
public void pleaseWaitDialogUpdate(int value) { pleaseWaitDialog.setProgress(value); } public void openPleaseWaitDialog() { if(pleaseWaitDialog == null) { pleaseWaitDialog = new ProgressDialog(LongTaskActivity.this); pleaseWaitDialog.setMessage("Please wait..."); pleaseWaitDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pleaseWaitDialog.setMax(10); pleaseWaitDialog.setCancelable(true); pleaseWaitDialog.show(); } }
quindi occorre modificare la gestione dell'avanzamento del task nel metodo doInBackground(String... input) della classe LongService:
@Override protected String doInBackground(String... input) { String in = input[0]; String out = ""; int size = in.length(); for (int i = 0 ; i < size; i++) { out += in.charAt(size-1-i); publishProgress((int)(10*(double)i/(double)size)); try { Thread.sleep(100); } catch (InterruptedException e) { } } return out; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); activity.pleaseWaitDialogUpdate(values[0]); }
Il metodo publishProgress(Integer... values) viene richiamato durante l'esecuzione del task per notificare l'avanzamento al metodo onProgressUpdate(Integer... values) che, in questo caso, richiama il metodo dell'activity principale che aggiorna la progressbar pleaseWaitDialogUpdate(int value). L'effetto risultante è quello mostrato di seguito:
Nessun commento:
Posta un commento