2012-08-28 2 views
27

Ich habe 2 ASyncTasks, man ruft einen Wert von einem httpPost und die anderen aktualisieren einige Elemente der Benutzeroberfläche (einschließlich einer Listview). Das Problem ist, da beide ASyncTasks den gleichen Hintergrundthread teilen, wenn die Netzwerkoperation zuerst startet und langsam läuft (wegen einer schlechten Netzwerkverbindung). Die anderen Hintergrund-Threads brauchen zu viel Zeit, um die App unverantwortlich zu machen.ASyncTasks blockiert andere

Da beide ASyncTasks unabhängig voneinander sind, ist es ziemlich dumm, den anderen zu warten. Es wäre logischer asynctasks verschiedene Klassen verwenden unterschiedliche Threads, bin ich falsch?

Lesen der ASyncTask doc. Gespräche über die Verwendung von executeOnExecutor(), aber wie kann ich das in einer API-Ebene niedriger als 11 lösen?

geht hier ein kleines Beispiel, das das "Problem"

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

Mit

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

Warum haben Sie eine AsyncTask, um die Benutzeroberfläche zu aktualisieren? Sie müssen das synchron auf dem Ii-Thread tun. Führt Ihre UI AsyncTask etwas anderes im Hintergrundthread aus? –

+0

Ich benutze es zum Beispiel um eine Listenansicht mit den Nachrichten zu zeigen. Im onPreExecute zeige ich die loading() UI, im Hintergrund die Nachrichten aus dem Internet und in der onPostExecute den Adapter der Listview zu. Ist dieser Ansatz falsch? – Addev

+0

@Addev: Warum starten Sie nicht einfach die zweite 'AsyncTask' in der' onPostExecute (...) 'Methode der ersten? Wenn das, was Sie brauchen, ist, dass eine AsyncTask auf dem Ergebnis des anderen beruht, dann haben Sie tatsächlich eine synchrone Anforderung und Sie sollten nicht zwei asynchrone Operationen parallel ausführen. – Squonk

Antwort

46

Dies ist, wie ich damit umgehen in meinem Code reproduziert:

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

Und ersetzen MyAsyncTask mit Ihren Task1 und Task2 jeweils. Grundsätzlich Änderung in AsyncTask erschien in Honeycomb (siehe Android SDK-Dokumentation here in "Reihenfolge der Ausführung" Abschnitt), also zuvor, starten Sie es wie üblich, für HC und up, verwenden Sie executeOnExecutor(), wenn Sie nicht neues Verhalten mögen (niemand tut, ich denke)

7

eine etwas allgemeinere Art und Weise, dies zu tun ist, zwei Hilfsmethoden in einer Utility-Klasse setzen, wie so:

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); 
     } 
    } 
} 

Dann können Sie Aufgaben ausführen mit Utils.execute(mytask) oder Utils.execute(mytask, params) und es kümmert sich um sie parallel ausführen.

+0

Danke. Hinweis für die Nachwelt, versuchen Sie nicht, AsyncTask.execute() 'zu überschreiben - Sie können nicht; es ist "endgültig". – TalkLittle

+0

Auch die erste Methode ist nicht notwendig und könnte tatsächlich zu unerwartetem Verhalten führen ('Parameter' können' null' anstelle eines leeren Arrays sein). Das Entfernen wird kompiliert/läuft auf Pre-Honeycomb-Geräten. – TalkLittle

+0

Es ist nicht unnötig, es spart Tipparbeit :) –

0

Das Problem besteht darin, dass jede AsyncTask im selben ThreadPoolExecutor ausgeführt wird, der in der API codiert ist. Dieser ThreadPoolExecutor kann abhängig von Ihrer Android-Version eine andere Anzahl von WorkerThread erstellen. Ich erinnere mich nicht an die Nummernversionen, aber die Idee ist, dass es in älteren Android-Versionen 1 WorkerThread war. Dann wurde es in späteren Versionen auf 5 aktualisiert. Und wurde kürzlich wieder auf 1 zurückgesetzt. Aus diesem Grund sind Ihre AsyncTasks blockiert. Sie laufen alle auf dem gleichen WorkerThread und somit werden sie nacheinander ausgeführt. Verwenden Sie executeOnExecutor mit THREAD_POOL_EXECUTOR, um eine echte parallele Ausführung zu erreichen. Sie können dies jedoch nur seit API 11 verwenden.