2012-11-27 13 views
5

Sto chiamando un asynctask dal mio thread dell'interfaccia utente al clic di un pulsante, che esegue alcune richieste HTTP-Get.Impedire l'esecuzione asynctask più volte

Cosa accadrebbe se un utente fa nuovamente clic sul pulsante prima che l'attività corrente sia completata? È gestito internamente da asynctask o devo occuparmene personalmente?

+1

un'istanza di AsyncTask non può essere eseguita più di una volta. – njzk2

+0

@ njzk2 Ogni clic sul pulsante crea tuttavia una nuova istanza. – Johan

+1

poi spetta a voi. Consiglio di disattivare il pulsante quando si fa clic e di riattivarlo solo quando è terminato l'asynctask (molto probabilmente in onPostExecute). Potresti anche avere una sola istanza asynctask. – njzk2

risposta

6

Ci sono due modi per farlo.

1: Mostra una finestra di dialogo di avanzamento e blocca l'utente per ulteriori input.

2: Crea la variabile AsyncTask in ambito privato. Quindi questo non funzionerà mai più di una volta.

+0

Capisco. Per quanto riguarda la tua seconda opzione: dovrei impostare asynctask sul mio metodo 'onCreate'e' execute() 'nel mio pulsante? – Johan

+0

@Johan se lo fai, non sarai più in grado di eseguirlo. avere un membro asynctask, quindi è possibile controllarne lo stato e, se è terminato, è possibile creare un nuovo asynctask e assegnarlo allo stesso membro. – lenik

+0

sì, puoi farlo. E controlla anche l'eccezione per Già in esecuzione. – Naeem

2

è possibile chiamare getStatus() sul proprio AsyncTask per verificare se è già in esecuzione e ignorare il clic del pulsante in tal caso.

per farlo è assolutamente necessario salvare un'istanza di AsyncTask da qualche parte e avere accesso ad esso.

+0

Ok, ma cosa succederebbe se fosse già in esecuzione e lo premo di nuovo? Non sembra lanciare eccezioni. – Johan

+0

è possibile mantenere un asynctask per ogni pulsante o qualcosa del genere, e se è già in esecuzione, basta ignorare il clic del pulsante. – lenik

1

Viene generato un altro AsyncTask che probabilmente causerà delle difficoltà - condizioni di gara con i dati salvati se lo si sta persistendo ecc. Raccomanderei di disabilitare il pulsante quando si riceve l'evento onClick e di visualizzare uno spinner di qualche descrizione che indica che qualcosa sta accadendo in background.

Abbiamo raggiunto qualcosa di simile con un pulsante di aggiornamento collocato in una barra delle azioni. Si noti che la nostra attività asincrona viene eseguita sotto un app.Service quindi durante una modifica della configurazione (dove hasStartedSync viene riavviata) ci affidiamo a un controllo "isTheSyncServiceRunning".

// on setting the selection buttons up 
public void onPrepareOptionsMenu(final Menu menu) { 
super.onPrepareOptionsMenu(menu); 
final MenuItem refreshItem = menu.findItem(id.sync); 
    if (isSynchronisationServiceRunning() || hasStartedSync) { 
    refreshItem.setEnabled(false); 
    if (Build.VERSION.SDK_INT > 10) { 
    // Replace the refresh icon with an indeterminate spinner for > 10 API. 
    final LayoutInflater inflater = (LayoutInflater)getSherlockActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    refreshItem.setActionView(inflater.inflate(layout.actionbar_indeterminate_progress, null)); 
    } 
} else { 
    refreshItem.setEnabled(true); 
    refreshItem.setActionView(null); 
} 
} 


// on selection. 
public boolean onOptionsItemSelected(final MenuItem item) { 
boolean isSelected; 
switch (item.getItemId()) { 
    case id.sync: 
    // Fire request to start GET here. 
    hasStartedSync = true; 
    invalidateOptionsMenu(); 
    break; 
    ... 
1

due modi:

  1. In onPreExeccute() di AsyncTask mostrano un ProgressDialog con il suo setCancelable(false). In onPostExecute() di AsyncTask chiama dismiss() su ProgressDialog. Questo mostrerà una finestra di dialogo di avanzamento del blocco. Puoi anche mostrare qualche messaggio o progresso qui.

  2. In onPreExeccute() di AsyncTask, chiamare setEnabled(false) sul pulsante. In onPostExecute() di AsyncTask chiama setEnabled(true) su Button. Questo disabiliterà il pulsante finché l'attività è in esecuzione. Puoi anche cambiare il testo del pulsante in questi punti. O anche temporaneamente sostituirlo con uno spinner di progresso (come in ActionBar).

Inoltre, è possibile attivare o disattivare il pulsante tra "Esegui" e "Annulla", come il pulsante di aggiornamento nei browser Web moderni. Ciò darà più controllo all'utente.

Una nota di cautela: Avvolgere tutto all'interno onDoInBackground() in un try-catch, in modo che AsyncTask esce sempre normalmente, e onPostExecute() viene sempre chiamato. Puoi giudicare dal parametro risultato di onPostExecute(result) se la richiesta HTTP ha ottenuto qualcosa o no.

Esempio: basta chiamare questo metodo per caricare un po 'di testo in un TextView dal server al clic di alcuni Button:

public static void runHTTP(final TextView targetView, final Button triggerBtn){ 

    //---new task---- 
    AsyncTask<Void,Void,String> task = new AsyncTask<Void, Void, String>(){ 
     @Override 
     protected void onPreExecute() { 
      super.onPreExecute(); 

      //----disable button--- 
      triggerBtn.setEnabled(false); 

      //---show message---- 
      targetView.setText("Loading..."); 
     } 

     @Override 
     protected void onPostExecute(String result) { 
      super.onPostExecute(result); 

      //----update result--- 
      targetView.setText(result); 

      //----re-enable button--- 
      triggerBtn.setEnabled(true); 
     } 

     @Override 
     protected String doInBackground(Void... voids) { 

      //---default value-- 
      String result = "No Data"; 

      //--for safety-- 
      try{ 

      //-----do time consuming stuff here --- 

      }catch (Exception e){ 

       //---error value-- 
       result = "Error fetching data"; 
      } 

      return result; 
     } 
    }; 

    //----run task---- 
    task.execute(); 
} 
+0

... hai visto le finestre di dialogo di avanzamento nell'app Google + o Gmail? ... modello migliore è quello di mettere da qualche parte il progresso indeterminato indeterminato e disabilitare l'interfaccia utente indesiderata (come questo pulsante) quindi -1 per la finestra di dialogo e +1 per il pulsante di disabilitazione – Selvin

+0

@Selvin Un'opzione è un'opzione :). Inoltre, Google ha creato 'ProgressDialog', quindi è meglio esserne utile in almeno alcune situazioni. –

+0

@Selvin Come quando il telefono si sta spegnendo, non possono rischiare di fare altro con l'interfaccia utente. –

4

Perché non utilizzare un flag booleano e verificare che sia stato? Uso questa logica nelle mie liste senza fine e non ha mai mancato di funzionare.

Toggle la bandiera nella doInBackground()

@Override 
protected Void doInBackground(Void... params) { 

    loadingData = true; 
} 

E poi di nuovo nel onPostExecute()

@Override 
protected void onPostExecute(Void result) { 

    // CHANGE THE LOADINGMORE STATUS TO PERMIT FETCHING MORE DATA 
    loadingData = false; 
} 

E per click ascoltatore del pulsante

your_btn.setOnClickListener(new OnClickListener() { 

    @Override 
    public void onClick(View v) { 

     if (loadingData == false) { 
      // RUN THE ASYNCTASK AGAIN 
     } else if (loadingData == true) { 
      // SHOW A TOAST THAT YOU ARE ALREADY FETCHING DATA 
     } 
    } 
}); 
+1

Se cancellate un 'AsyncTask' (chiamando' cancel'), dovrete anche attivare il flag in 'onCancelled', poiché' onPostExecute' non verrà chiamato. –

+0

@ dave.c: è vero. Ma nel contesto della domanda, ho sentito che la risposta era sufficiente. –

+0

Non penso che funzionerà. Il valore 'loadingData' sarà diverso ogni volta che lo cambi in un thread diverso a causa del nuovo' AsyncTask'. –