2012-08-28 15 views
27

Ho 2 ASyncTasks, uno recupera un valore da un httpPost e l'altro aggiorna alcuni elementi dell'interfaccia utente (inclusa una list list). Il problema è che poiché entrambi gli ASyncTasks condividono lo stesso thread in background, se l'opperazione della rete inizia per prima e viene eseguita lentamente (a causa di una cattiva connettività di rete). Gli altri thread in background impiegano troppo tempo a rendere l'app irresponsabile.ASyncTask che bloccano gli altri

Poiché entrambi i cicli ASync sono indipendenti, è piuttosto stupido da fare aspettare l'altro. Sarebbe più logico asincronizzare diverse classi usano thread diversi, sbaglio?

Lettura del ASyncTask doc. Parla dell'utilizzo di executeOnExecutor(), ma come posso risolverlo in un livello API inferiore a 11?

qui va un piccolo esempio che riproduce il "problema"

 new Task1().execute(); 
     new Task2().execute(); 

Con

public class Task1 extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected Void doInBackground(Void... params) { 
     GLog.e("doInBackground start 1"); 
     SystemClock.sleep(9000); 
     GLog.e("doInBackground end 1"); 
     return null; 
    } 

    @Override 
    protected void onPreExecute() { 
     GLog.e("onPreExecute 1"); 
     super.onPreExecute(); 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     GLog.e("onPostExecute 1"); 
     super.onPostExecute(result); 
    } 

} 

public class Task2 extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected void onPreExecute() { 
     GLog.e("onPreExecute 2"); 
     super.onPreExecute(); 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
     GLog.e("doInBackground start 2"); 
     SystemClock.sleep(9000); 
     GLog.e("doInBackground end 2"); 
     return null; 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     GLog.e("onPostExecute 2"); 
     super.onPostExecute(result); 
    } 

} 
+0

Perché hai un AsyncTask per aggiornare l'interfaccia utente? È necessario farlo in modo sincrono sul thread Ii. Il tuo UI AsyncTask fa qualcos'altro sul thread in background? –

+0

Lo ad esempio per mostrare una listview con le notizie. In onPreExecute mostro l'interfaccia utente di caricamento(), in background recupera le notizie da Internet e in onPostExecute assegna l'adattatore a listview. Questo approccio è errato? – Addev

+0

@Addev: Perché non inizi il secondo 'AsyncTask' nel metodo' onPostExecute (...) 'del primo? Se ciò di cui hai bisogno è che un 'AsyncTask' si basa sul risultato dell'altro, hai effettivamente un requisito sincrono e non dovresti eseguire due operazioni asincrone in parallelo. – Squonk

risposta

46

Questo è come mi occupo di questo nel mio codice:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
    new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
} else { 
    new MyAsyncTask().execute(); 
} 

e sostituire MyAsyncTask con il tuo Task1 e Task2 rispettivamente. Fondamentalmente il cambiamento in AsyncTask è apparso in Honeycomb (vedi i documenti SDK Android here nella sezione "Ordine di esecuzione"), quindi prima che lo avvii come al solito, per HC e su, usa executeOnExecutor() se non ti piace il nuovo comportamento (nessuno lo fa, credo)

7

un modo un po 'più generale per farlo è quello di mettere due metodi di supporto in una classe di utilità in questo modo:

class Utils { 

    @SuppressLint("NewApi") 
    public static <P, T extends AsyncTask<P, ?, ?>> void execute(T task, P... params) { 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
      task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
     } else { 
      task.execute(params); 
     } 
    } 
} 

quindi è possibile eseguire compiti con Utils.execute(mytask) o Utils.execute(mytask, params) e si prenderà cura di eseguendoli in parallelo.

+0

Grazie. Nota per i posteri, non preoccuparti di provare a sovrascrivere 'AsyncTask.execute()' - non puoi; è 'finale'. – TalkLittle

+0

Anche il primo metodo non è necessario e potrebbe effettivamente causare un comportamento imprevisto ('params' può essere' null' invece di un array vuoto). Rimuovendolo compila/gira bene su dispositivi pre-Honeycomb. – TalkLittle

+0

Non è necessario, salva la digitazione :) –

0

Il problema è che ogni AsyncTask viene eseguito nello stesso ThreadPoolExecutor che è codificato nell'API. Questo ThreadPoolExecutor può creare un diverso numero di WorkerThread a seconda della versione di Android. Non ricordo le versioni dei numeri, ma l'idea è che nelle versioni precedenti di Android era 1 WorkerThread. Quindi è stato aggiornato a 5 nelle versioni successive. E recentemente è tornato a 1 di nuovo. Questo è il motivo per cui i tuoi AsyncTasks sono bloccati. Corrono tutti sullo stesso WorkerThread e quindi eseguono in sequenza. Prova a utilizzare executeOnExecutor con THREAD_POOL_EXECUTOR per ottenere l'effettiva esecuzione parallela. Tuttavia, puoi utilizzare questo solo dall'API 11.

Problemi correlati