Esecuzione di più AsyncTask contemporaneamente - non è possibile?


259

Sto cercando di eseguire due AsyncTask contemporaneamente. (La piattaforma è Android 1.5, HTC Hero.) Tuttavia, viene eseguito solo il primo. Ecco un semplice frammento per descrivere il mio problema:

public class AndroidJunk extends Activity {
 class PrinterTask extends AsyncTask<String, Void, Void> {
     protected Void doInBackground(String ... x) {
      while (true) {
       System.out.println(x[0]);
       try {
        Thread.sleep(1000);
       } catch (InterruptedException ie) {
        ie.printStackTrace();
       }
      }
        }
    };

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

        new PrinterTask().execute("bar bar bar");
        new PrinterTask().execute("foo foo foo");

        System.out.println("onCreate() is done.");
    }
}

L'output che mi aspetto è:

onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo

E così via. Tuttavia, quello che ottengo è:

onCreate() is done.
bar bar bar
bar bar bar
bar bar bar

Il secondo AsyncTask non viene mai eseguito. Se cambio l'ordine delle istruzioni execute (), solo l'attività foo produrrà output.

Mi sto perdendo qualcosa di ovvio qui e / o facendo qualcosa di stupido? Non è possibile eseguire due AsyncTask contemporaneamente?

Modifica: ho realizzato che il telefono in questione funziona con Android 1.5, ho aggiornato il problema descr. di conseguenza. Non ho questo problema con un HTC Hero con Android 2.1. Hmmm ...


Il tuo codice funziona per me, quindi il problema deve essere altrove. Hai inserito un filtro nella vista LogCat? ;-)
mreichelt,

Hm, è strano. Non ho alcun filtro in logcat. Stai usando anche 1.6? In tal caso, quale telefono?
rodione

Oops, ho appena realizzato che è in esecuzione (antico) Android 1.5
rodione

1
Ho usato Android 1.6 come target e un emulatore Android 2.1. Quindi, se il problema si verifica davvero solo su un HTC Hero con Android 1.5: avvitali, stai bene. ;-) HTC Hero ha già l'aggiornamento a una versione Android più recente. Non mi preoccuperei se ci fossero alcuni produttori che rovinano le cose. Inoltre non mi dispiacerebbe più di Android 1.5.
mreichelt,

AsyncTask deve essere utilizzato per attività di durata più breve di 5 ms. Passa a ThreadPoolExecutor ( developer.android.com/reference/java/util/concurrent/… ). Articolo correlato: stackoverflow.com/questions/6964011/…
Ravindra babu,

Risposte:


438

AsyncTask utilizza un modello di pool di thread per eseguire le cose da doInBackground (). Il problema è inizialmente (nelle prime versioni del sistema operativo Android) che la dimensione del pool era solo 1, il che significa che non c'erano calcoli paralleli per un gruppo di AsyncTasks. Ma in seguito lo hanno risolto e ora la dimensione è 5, quindi al massimo 5 AsyncTask possono essere eseguiti contemporaneamente. Sfortunatamente non ricordo in quale versione esattamente l'hanno cambiata.

AGGIORNARE:

Ecco cosa dice l'attuale API (27-01-2012) su questo:

Alla prima introduzione, gli AsyncTask erano eseguiti in serie su un singolo thread in background. A partire da DONUT, questo è stato modificato in un pool di thread che consente a più attività di operare in parallelo. Dopo HONEYCOMB, si prevede di riportarlo in un singolo thread per evitare errori di applicazione comuni causati da un'esecuzione parallela. Se si desidera veramente l'esecuzione parallela, è possibile utilizzare la versione executeOnExecutor (Executor, Params ...) di questo metodo con THREAD_POOL_EXECUTOR; tuttavia, vedere i commenti lì per avvertenze sul suo utilizzo.

DONUT è Android 1.6, HONEYCOMB è Android 3.0.

AGGIORNAMENTO: 2

Vedi il commento di kabukoda Mar 7 2012 at 1:27.

Si scopre che per le API in cui viene utilizzato "un pool di thread che consente a più attività di operare in parallelo" (a partire da 1.6 e terminando su 3.0) il numero di AsyncTasks in esecuzione simultaneamente dipende da quante attività sono già state passate per l'esecuzione, ma non hanno ancora finito il loro doInBackground().

Questo è testato / confermato da me il 2.2. Supponiamo di avere un AsyncTask personalizzato che dorme solo un secondo doInBackground(). AsyncTasks utilizza una coda di dimensioni fisse internamente per la memorizzazione di attività ritardate. La dimensione della coda è 10 per impostazione predefinita. Se inizi 15 attività personalizzate di seguito, le prime 5 inseriranno le loro doInBackground(), ma il resto attenderà in coda un thread di lavoro gratuito. Non appena uno dei primi 5 termina e quindi rilascia un thread di lavoro, verrà avviata un'attività dalla coda. Quindi, in questo caso, verranno eseguite contemporaneamente al massimo 5 attività. Tuttavia, se avvii 16 attività personalizzate di seguito, le prime 5 entreranno nelle loro doInBackground(), le restanti 10 entreranno in coda, ma per il 16 verrà creato un nuovo thread di lavoro in modo che inizi l'esecuzione immediatamente. Quindi, in questo caso, verranno eseguite contemporaneamente al massimo 6 attività.

Esiste un limite al numero di attività che possono essere eseguite contemporaneamente. Poiché AsyncTaskutilizza un esecutore di pool di thread con un numero massimo limitato di thread di lavoro (128) e la coda delle attività in ritardo ha dimensioni fisse 10, se si tenta di eseguire più di 138 attività personalizzate con cui si bloccherà l'app java.util.concurrent.RejectedExecutionException.

A partire dalla 3.0 l'API consente di utilizzare il proprio esecutore del pool di thread personalizzato tramite il AsyncTask.executeOnExecutor(Executor exec, Params... params)metodo. Ciò consente, ad esempio, di configurare la dimensione della coda delle attività ritardate se il valore predefinito 10 non è quello che ti serve.

Come menziona @Knossos, esiste un'opzione da utilizzare AsyncTaskCompat.executeParallel(task, params);dalla libreria di supporto v.4 per eseguire attività in parallelo senza preoccuparsi del livello API. Questo metodo è diventato obsoleto nel livello API 26.0.0.

AGGIORNAMENTO: 3

Ecco una semplice app di prova per giocare con il numero di compiti, esecuzione seriale o parallela: https://github.com/vitkhudenko/test_asynctask

AGGIORNAMENTO: 4 (grazie @penkzhou per averlo segnalato)

A partire da Android 4.4 AsyncTasksi comporta diversamente da quanto descritto nella sezione AGGIORNAMENTO: 2 . Esiste una correzione per impedire la AsyncTaskcreazione di troppi thread.

Prima di Android 4.4 (API 19) AsyncTaskc'erano i seguenti campi:

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(10);

In Android 4.4 (API 19) i campi sopra riportati sono cambiati in questo:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

Questa modifica aumenta la dimensione della coda a 128 elementi e riduce il numero massimo di thread al numero di core della CPU * 2 + 1. Le app possono comunque inviare lo stesso numero di attività.


11
Solo FYI, la dimensione del pool principale è 5, ma il massimo è 128. Ciò significa che se si riempie la coda, possono essere eseguiti contemporaneamente più di 5 (fino a 128).
Kabuko

2
@kabuko: hai assolutamente ragione. Ho appena esaminato questo su 2.2 e confermando che se 5 attività sono già in esecuzione (la dimensione del pool principale è 5), quindi inizia a mettere il resto delle attività nella coda delle attività ritardate, tuttavia se la coda è già piena (la dimensione massima della coda è 10 ), quindi avvia un thread di lavoro aggiuntivo per l'attività in modo che l'attività venga avviata immediatamente.
Vit Khudenko,

1
mi ha appena salvato la giornata, eroe della didascalia :) Grazie
Karoly,

2
Aggiorna questa risposta per le nuove funzioni compat . Esempio:AsyncTaskCompat.executeParallel(task, params);
Cnosso

1
@Arhimed Per favore, aggiorna la risposta dal momento che AsyncTaskCompat.executeParallel(task, params);è ora deprecato
Kirill Starostin

218

Ciò consente l'esecuzione parallela su tutte le versioni di Android con API 4+ (Android 1.6+):

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
void startMyTask(AsyncTask asyncTask) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}

Questo è un riassunto dell'ottima risposta di Arhimed.

Assicurati di utilizzare il livello API 11 o superiore come obiettivo di creazione del progetto. In Eclipse, cioè Project > Properties > Android > Project Build Target. Ciò non interromperà la retrocompatibilità con livelli API inferiori. Non preoccuparti, otterrai errori Lint se utilizzi accidentalmente funzionalità introdotte in seguito minSdkVersion. Se desideri davvero utilizzare le funzionalità introdotte successivamente minSdkVersion, puoi eliminare tali errori utilizzando le annotazioni, ma in tal caso, devi occuparti della compatibilità da solo . Questo è esattamente ciò che è accaduto nel frammento di codice sopra.


3
Ho dovuto cambiare TimerTask.THREAD_POOL_EXECUTOR in AsynTask.THREAD_POOL_EXECUTOR quindi questo esempio funziona
Ronnie

2
Per annullare i problemi puoi rendere generica la chiamata: void startMyTask(AsyncTask<Void, ?, ?> asyncTask) (se il tuo doInBackground prende il vuoto)
Peter

Puoi semplificare ulteriormente il codice sopra se la tua classe estende AsyncTask <Void, Integer, Void>. Ha funzionato alla grande sulai, grazie.
Peter Arandorenko,

1
Molte grazie. Ho avuto uno scenario in cui ho bisogno di eseguire due attività asincrone contemporaneamente per leggere i dati dal server Web, ma ho scoperto che il server riceve solo un URL e fintanto che quella particolare richiesta non viene completata, l'url del secondo AsyncTask non colpirà il server . La tua risposta ha risolto il mio problema :)
Santhosh Gutta,

1
Continuo a ricevere voti per questo, grazie. :) Ma ho l'impressione che molte persone provino ad usare AsyncTask per il networking in cui librerie eccellenti come Volley , Glide o Swagger sono scelte migliori. Assicurati di controllare questi prima di usare AsyncTask :)
sulai,

21

Rendere il suggerimento @sulai più generico:

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> asyncTask, T... params) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}   

soluzione più generale: AsyncTaskCompat.executeParallel (nuovo MyAsyncTask, myprams);
Vikash Kumar Verma,

11

Giusto per includere l'ultimo aggiornamento (AGGIORNAMENTO 4) nella risposta immacolata di @Arhimed nell'ottimo riassunto di @sulai:

void doTheTask(AsyncTask task) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4 (API 19) and above
        // Parallel AsyncTasks are possible, with the thread-pool size dependent on device
        // hardware
        task.execute(params);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Android 3.0 to
        // Android 4.3
        // Parallel AsyncTasks are not possible unless using executeOnExecutor
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    } else { // Below Android 3.0
        // Parallel AsyncTasks are possible, with fixed thread-pool size
        task.execute(params);
    }
}

Oh wow, fantastico. Questo significa che .executeOnExecutor()ora è ridondante per API> = KITKAT, sì?
Taslim Oseni,


3

È possibile. La mia versione del dispositivo Android è 4.0.4 e android.os.Build.VERSION.SDK_INT è 15

Ho 3 filatori

Spinner c_fruit=(Spinner) findViewById(R.id.fruits);
Spinner c_vegetable=(Spinner) findViewById(R.id.vegetables);
Spinner c_beverage=(Spinner) findViewById(R.id.beverages);

E ho anche una classe Async-Tack.

Ecco il mio codice di caricamento del filatore

RequestSend reqs_fruit = new RequestSend(this);
reqs_fruit.where="Get_fruit_List";
reqs_fruit.title="Loading fruit";
reqs_fruit.execute();

RequestSend reqs_vegetable = new RequestSend(this);
reqs_vegetable.where="Get_vegetable_List";
reqs_vegetable.title="Loading vegetable";
reqs_vegetable.execute();

RequestSend reqs_beverage = new RequestSend(this);
reqs_beverage.where="Get_beverage_List";
reqs_beverage.title="Loading beverage";
reqs_beverage.execute();

Funziona perfettamente. Uno ad uno i miei filatori caricati. Non ho eseguito executeOnExecutor.

Ecco la mia classe di attività asincrona

public class RequestSend  extends AsyncTask<String, String, String > {

    private ProgressDialog dialog = null;
    public Spinner spin;
    public String where;
    public String title;
    Context con;
    Activity activity;      
    String[] items;

    public RequestSend(Context activityContext) {
        con = activityContext;
        dialog = new ProgressDialog(activityContext);
        this.activity = activityContext;
    }

    @Override
    protected void onPostExecute(String result) {
        try {
            ArrayAdapter<String> adapter = new ArrayAdapter<String> (activity, android.R.layout.simple_spinner_item, items);       
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            spin.setAdapter(adapter);
        } catch (NullPointerException e) {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        } catch (Exception e)  {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
        super.onPostExecute(result);

        if (dialog != null)
            dialog.dismiss();   
    }

    protected void onPreExecute() {
        super.onPreExecute();
        dialog.setTitle(title);
        dialog.setMessage("Wait...");
        dialog.setCancelable(false); 
        dialog.show();
    }

    @Override
    protected String doInBackground(String... Strings) {
        try {
            Send_Request();
            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        return null;
    }

    public void Send_Request() throws JSONException {

        try {
            String DataSendingTo = "http://www.example.com/AppRequest/" + where;
            //HttpClient
            HttpClient httpClient = new DefaultHttpClient();
            //Post header
            HttpPost httpPost = new HttpPost(DataSendingTo);
            //Adding data
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

            nameValuePairs.add(new BasicNameValuePair("authorized","001"));

            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // execute HTTP post request
            HttpResponse response = httpClient.execute(httpPost);

            BufferedReader reader;
            try {
                reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                StringBuilder builder = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    builder.append(line) ;
                }

                JSONTokener tokener = new JSONTokener(builder.toString());
                JSONArray finalResult = new JSONArray(tokener);
                items = new String[finalResult.length()]; 
                // looping through All details and store in public String array
                for(int i = 0; i < finalResult.length(); i++) {
                    JSONObject c = finalResult.getJSONObject(i);
                    items[i]=c.getString("data_name");
                }

            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2
come vedi che non vengono eseguiti in sequenza?
Behelit,

@behelit Importa quando funziona? Si prega di leggere risposta accettata per maggiori dettagli stackoverflow.com/a/4072832/2345900~~V~~plural~~3rd
Sajitha Rathnayake

2
Scusa non capisco. Se non riesci a capire se i tuoi asincroni sono sequenziali o veramente asincroni, è importante. Inoltre, la risposta collegata dice di usare il metodo executeonexecutor, che in realtà non stai usando nella tua risposta. Scusa se mi sono perso qualcosa.
Behelit,

1
"Uno ad uno i miei filatori caricati." Hai letteralmente detto tu stesso che si tratta di un'esecuzione seriale, non di un'esecuzione parallela. Ognuno dei nostri AsyncTask è aggiunto a una coda ed elaborato in sequenza. Ciò non risponde alla domanda del PO.
Joshua Pinter,

Questa è una risposta molto vecchia. Se questo non funziona per te, si prega di fare riferimento alla risposta accettata
Sajitha Rathnayake,

3

se si desidera eseguire attività parallele, è necessario chiamare il metodo executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")dopo la versione Android 3.0; ma questo metodo non esiste prima di Android 3.0 e dopo 1.6 perché viene eseguito in parallelo da solo, quindi ti suggerisco di personalizzare la tua classe AsyncTask nel tuo progetto, per evitare eccezioni in diverse versioni di Android.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.