Ich versuche, zwei AsyncTasks gleichzeitig auszuführen. (Plattform ist Android 1.5, HTC Hero.) Es wird jedoch nur die erste ausgeführt. Hier ist ein einfacher Ausschnitt, um mein Problem zu beschreiben:
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.");
}
}
Die Ausgabe, die ich erwarte, ist:
onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo
Und so weiter. Was ich jedoch bekomme, ist:
onCreate() is done.
bar bar bar
bar bar bar
bar bar bar
Die zweite AsyncTask wird nie ausgeführt. Wenn ich die Reihenfolge der Anweisungen execute () ändere, wird nur die Aufgabe foo ausgegeben.
Vermisse ich hier etwas Offensichtliches und / oder mache ich etwas Dummes? Ist es nicht möglich, zwei AsyncTasks gleichzeitig auszuführen?
Bearbeiten: Ich habe festgestellt, dass auf dem betreffenden Telefon Android 1.5 ausgeführt wird. Ich habe die Problembeschreibung aktualisiert. entsprechend. Ich habe dieses Problem nicht mit einem HTC Hero unter Android 2.1. Hmmm ...
Antworten:
AsyncTask verwendet ein Thread-Pool-Muster zum Ausführen des Materials von doInBackground (). Das Problem ist zunächst (in frühen Android-Betriebssystemversionen), dass die Poolgröße nur 1 betrug, was bedeutet, dass für eine Reihe von AsyncTasks keine parallelen Berechnungen durchgeführt wurden. Aber später haben sie das behoben und jetzt ist die Größe 5, so dass höchstens 5 AsyncTasks gleichzeitig ausgeführt werden können. Leider kann ich mich nicht erinnern, in welcher Version sie das genau geändert haben.
AKTUALISIEREN:
Hier ist, was die aktuelle (2012-01-27) API dazu sagt:
DONUT ist Android 1.6, HONEYCOMB ist Android 3.0.
UPDATE: 2
Siehe den Kommentar von
kabuko
vonMar 7 2012 at 1:27
.Es stellt sich heraus, dass für APIs, bei denen "ein Pool von Threads verwendet wird, bei denen mehrere Aufgaben parallel ausgeführt werden können" (beginnend mit 1.6 und endend mit 3.0), die Anzahl der gleichzeitig ausgeführten AsyncTasks davon abhängt, wie viele Aufgaben bereits zur Ausführung übergeben wurden habe ihre
doInBackground()
noch nicht beendet .Dies wird von mir am 2.2 getestet / bestätigt. Angenommen, Sie haben eine benutzerdefinierte AsyncTask, die nur eine Sekunde lang schläft
doInBackground()
. AsyncTasks verwenden intern eine Warteschlange mit fester Größe zum Speichern verzögerter Aufgaben. Die Warteschlangengröße beträgt standardmäßig 10. Wenn Sie 15 Ihrer benutzerdefinierten Aufgaben hintereinander starten, geben die ersten 5 ihre eindoInBackground()
, während der Rest in einer Warteschlange auf einen freien Arbeitsthread wartet. Sobald eine der ersten 5 beendet ist und somit ein Arbeitsthread freigegeben wird, beginnt die Ausführung einer Aufgabe aus der Warteschlange. In diesem Fall werden also höchstens 5 Aufgaben gleichzeitig ausgeführt. Wenn Sie jedoch 16 Ihre benutzerdefinierten Aufgaben hintereinander starten, werden zuerst 5 eingegebendoInBackground()
, die restlichen 10 werden in die Warteschlange gestellt. Für den 16. wird jedoch ein neuer Arbeitsthread erstellt, sodass die Ausführung sofort gestartet wird. In diesem Fall werden also höchstens 6 Aufgaben gleichzeitig ausgeführt.Es gibt eine Begrenzung, wie viele Aufgaben gleichzeitig ausgeführt werden können. Da
AsyncTask
ein Thread-Pool-Executor mit einer begrenzten maximalen Anzahl von Worker-Threads (128) verwendet wird und die Warteschlange für verzögerte Aufgaben die feste Größe 10 hat, stürzt die App ab, wenn Sie versuchen, mehr als 138 Ihrer benutzerdefinierten Aufgaben auszuführenjava.util.concurrent.RejectedExecutionException
.Ab 3.0 ermöglicht die API die Verwendung Ihres benutzerdefinierten Thread-Pool-Executors über die
AsyncTask.executeOnExecutor(Executor exec, Params... params)
Methode. Auf diese Weise können Sie beispielsweise die Größe der Warteschlange für verzögerte Aufgaben konfigurieren, wenn Sie nicht die Standardeinstellung 10 benötigen.Wie @Knossos erwähnt, gibt es eine Option
AsyncTaskCompat.executeParallel(task, params);
aus der Support v.4-Bibliothek, um Aufgaben parallel auszuführen, ohne sich um die API-Ebene zu kümmern . Diese Methode wurde in API-Level 26.0.0 nicht mehr unterstützt.UPDATE: 3
Hier ist eine einfache Test-App zum Spielen mit einer Reihe von Aufgaben, serielle oder parallele Ausführung: https://github.com/vitkhudenko/test_asynctask
UPDATE: 4 (danke @penkzhou für den Hinweis)
Ab Android 4.4
AsyncTask
verhält es sich anders als im Abschnitt UPDATE: 2 beschrieben . Es gibt eine Korrektur , um zu verhindern,AsyncTask
dass zu viele Threads erstellt werden.Vor Android 4.4 (API 19) gab
AsyncTask
es folgende Felder:In Android 4.4 (API 19) werden die obigen Felder folgendermaßen geändert:
Diese Änderung erhöht die Größe der Warteschlange auf 128 Elemente und reduziert die maximale Anzahl von Threads auf die Anzahl von CPU-Kernen * 2 + 1. Apps können weiterhin dieselbe Anzahl von Aufgaben senden.
quelle
AsyncTaskCompat.executeParallel(task, params);
AsyncTaskCompat.executeParallel(task, params);
jetzt veraltet istDies ermöglicht die parallele Ausführung auf allen Android-Versionen mit API 4+ (Android 1.6+):
Dies ist eine Zusammenfassung von Arhimeds ausgezeichneter Antwort.
Stellen Sie sicher, dass Sie API Level 11 oder höher als Projekterstellungsziel verwenden. In Eclipse also
Project > Properties > Android > Project Build Target
. Dadurch wird die Abwärtskompatibilität mit niedrigeren API-Ebenen nicht beeinträchtigt. Keine Sorge, Sie erhalten Lint-Fehler, wenn Sie versehentlich Funktionen verwenden, die später als eingeführt wurdenminSdkVersion
. Wenn Sie wirklich als Funktionen nutzen eingeführt später möchtenminSdkVersion
, können Sie diese Fehler mit Anmerkungen zu unterdrücken, aber in diesem Fall müssen Sie kümmern sich um die Kompatibilität nehmen selbst . Genau das ist im obigen Code-Snippet passiert.quelle
void startMyTask(AsyncTask<Void, ?, ?> asyncTask)
stornieren, können Sie den Anruf generisch machen: (wenn Ihr doInBackground Void nimmt)@ Sulai-Vorschlag allgemeiner gestalten:
quelle
Nur um das neueste Update (UPDATE 4) in @Arhimeds makellose Antwort in die sehr gute Zusammenfassung von @sulai aufzunehmen:
quelle
.executeOnExecutor()
ist jetzt redundant für API> = KITKAT, ja?Das Beispiel für Android-Entwickler zum effizienten Laden von Bitmaps verwendet eine benutzerdefinierte Asynctask (aus Jellybean kopiert), sodass Sie den executeOnExecutor in APIs unter <11 verwenden können
http://developer.android.com/training/displaying-bitmaps/index.html
Laden Sie den Code herunter und gehen Sie zum Util-Paket.
quelle
Es ist möglich. Meine Android-Geräteversion ist 4.0.4 und android.os.Build.VERSION.SDK_INT ist 15
Ich habe 3 Spinner
Und ich habe auch eine Async-Tack-Klasse.
Hier ist mein Spinner-Ladecode
Das funktioniert perfekt. Einer nach dem anderen luden meine Spinner. Ich habe den Benutzer executeOnExecutor nicht verwendet.
Hier ist meine Async-Task-Klasse
quelle
Wenn Sie Aufgaben parallel ausführen möchten, müssen Sie die Methode
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")
nach der Android-Version 3.0 aufrufen . Diese Methode ist jedoch vor Android 3.0 und nach 1.6 nicht vorhanden, da sie von selbst parallel ausgeführt wird. Ich empfehle daher, dass Sie Ihre eigene AsyncTask-Klasse in Ihrem Projekt anpassen, um Ausnahmen in anderen Android-Versionen zu vermeiden.quelle