Próbuję uruchomić dwa AsyncTasks jednocześnie. (Platforma to Android 1.5, HTC Hero.) Jednak uruchamiany jest tylko pierwszy. Oto prosty fragment opisujący mój problem:
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.");
}
}
Oczekiwany wynik to:
onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo
I tak dalej. Dostaję jednak:
onCreate() is done.
bar bar bar
bar bar bar
bar bar bar
Drugi AsyncTask nigdy nie zostanie wykonany. Jeśli zmienię kolejność instrukcji execute (), tylko zadanie foo wygeneruje dane wyjściowe.
Czy brakuje mi czegoś oczywistego i / lub robię coś głupiego? Czy nie jest możliwe jednoczesne uruchomienie dwóch AsyncTasks?
Edycja: Zdałem sobie sprawę, że dany telefon działa pod kontrolą Androida 1.5, zaktualizowałem opis problemu. odpowiednio. Nie mam tego problemu z telefonem HTC Hero z systemem Android 2.1. Hmmm ...
Odpowiedzi:
AsyncTask używa wzorca puli wątków do uruchamiania rzeczy z doInBackground (). Problem polega na tym, że początkowo (we wczesnych wersjach systemu operacyjnego Android) wielkość puli wynosiła zaledwie 1, co oznacza brak równoległych obliczeń dla grupy AsyncTasks. Ale później to naprawili i teraz rozmiar wynosi 5, więc maksymalnie 5 zadań AsyncTask może działać jednocześnie. Niestety nie pamiętam, w jakiej wersji dokładnie to zmienili.
AKTUALIZACJA:
Oto, co mówi na ten temat bieżący (2012-01-27) interfejs API:
DONUT to Android 1.6, HONEYCOMB to Android 3.0.
AKTUALIZACJA: 2
Zobacz komentarz
kabuko
odMar 7 2012 at 1:27
.Okazuje się, że w przypadku interfejsów API, w których używana jest „pula wątków umożliwiająca równoległe działanie wielu zadań” (od wersji 1.6 do wersji 3.0) liczba jednoczesnych uruchomień AsyncTasks zależy od liczby zadań, które zostały już przekazane do wykonania, ale jeszcze nie skończyli
doInBackground()
.To zostało przetestowane / potwierdzone przeze mnie w wersji 2.2. Załóżmy, że masz niestandardową funkcję AsyncTask, która tylko śpi sekundę
doInBackground()
. AsyncTasks używają wewnętrznie kolejki o stałym rozmiarze do przechowywania opóźnionych zadań. Rozmiar kolejki to domyślnie 10. Jeśli rozpoczniesz 15 niestandardowych zadań z rzędu, pierwsze 5 wprowadzi jedoInBackground()
, ale reszta będzie czekać w kolejce na wolny wątek roboczy. Gdy tylko jedna z pierwszych 5 zakończy się, a tym samym zwolni wątek roboczy, zadanie z kolejki rozpocznie wykonywanie. Tak więc w tym przypadku jednocześnie uruchomi się maksymalnie 5 zadań. Jeśli jednak uruchomisz 16 niestandardowych zadań z rzędu, pierwsze 5 wprowadzi jedoInBackground()
, a pozostałe 10 dostanie się do kolejki, ale dla 16. zostanie utworzony nowy wątek roboczy, aby natychmiast rozpocząć wykonywanie. Tak więc w tym przypadku jednocześnie uruchomi się maksymalnie 6 zadań.Istnieje limit liczby zadań, które można uruchomić jednocześnie. Ponieważ
AsyncTask
używa modułu wykonującego pulę wątków z ograniczoną maksymalną liczbą wątków roboczych (128), a kolejka zadań opóźnionych ma ustalony rozmiar 10, próba wykonania więcej niż 138 niestandardowych zadań spowoduje awarię aplikacjijava.util.concurrent.RejectedExecutionException
.Począwszy od wersji 3.0 interfejs API pozwala na użycie niestandardowego modułu wykonującego pulę wątków za pomocą
AsyncTask.executeOnExecutor(Executor exec, Params... params)
metody. Pozwala to na przykład skonfigurować rozmiar kolejki opóźnionych zadań, jeśli domyślna wartość 10 nie jest potrzebna.Jak wspomina @Knossos, istnieje możliwość korzystania
AsyncTaskCompat.executeParallel(task, params);
z biblioteki wsparcia v.4 do równoległego uruchamiania zadań bez zawracania sobie głowy poziomem API. Ta metoda stała się przestarzała na poziomie interfejsu API 26.0.0.AKTUALIZACJA: 3
Oto prosta aplikacja testowa do grania z wieloma zadaniami, wykonywanie szeregowe vs. równoległe: https://github.com/vitkhudenko/test_asynctask
AKTUALIZACJA: 4 (dzięki @penkzhou za zwrócenie na to uwagi)
Począwszy od Androida 4.4
AsyncTask
zachowuje się inaczej niż opisano w sekcji AKTUALIZACJA: 2 . Istnieje poprawka zapobiegającaAsyncTask
tworzeniu zbyt wielu wątków.Przed Androidem 4.4 (API 19)
AsyncTask
miał następujące pola:W Androidzie 4.4 (API 19) powyższe pola zostały zmienione na:
Ta zmiana zwiększa rozmiar kolejki do 128 elementów i zmniejsza maksymalną liczbę wątków do liczby rdzeni procesora * 2 + 1. Aplikacje mogą nadal przesyłać tę samą liczbę zadań.
źródło
AsyncTaskCompat.executeParallel(task, params);
AsyncTaskCompat.executeParallel(task, params);
jest już przestarzałaPozwala to na równoległe wykonywanie na wszystkich wersjach Androida z API 4+ (Android 1.6+):
To jest streszczenie doskonałej odpowiedzi Arhimed.
Upewnij się, że używasz interfejsu API poziomu 11 lub wyższego jako celu kompilacji projektu. To znaczy w Eclipse
Project > Properties > Android > Project Build Target
. Nie wpłynie to na zgodność wsteczną na niższe poziomy API. Nie martw się, otrzymasz błędy Lint, jeśli przypadkowo użyjesz funkcji wprowadzonych późniejminSdkVersion
. Jeśli naprawdę chcesz korzystać z funkcji wprowadzonych później niżminSdkVersion
można tłumić te błędy za pomocą adnotacji, ale w tym przypadku trzeba zadbać o zgodność siebie . Dokładnie tak się stało w powyższym fragmencie kodu.źródło
void startMyTask(AsyncTask<Void, ?, ?> asyncTask)
ustawić ogólne wywołanie: (jeśli Twój doInBackground przyjmuje Void)Bardziej ogólne sugestie @sulai:
źródło
Wystarczy dołączyć najnowszą aktualizację (UPDATE 4) do nieskazitelnej odpowiedzi @Arhimed w bardzo dobrym streszczeniu @sulai:
źródło
.executeOnExecutor()
jest teraz redundantny dla API> = KITKAT, tak?Przykład programistów Androida ładujących bitmapy efektywnie wykorzystuje niestandardową asynchronię (skopiowaną z galaretki), abyś mógł używać executeOnExecutor w apis niższych niż <11
http://developer.android.com/training/displaying-bitmaps/index.html
Pobierz kod i przejdź do pakietu util.
źródło
Jest to możliwe. Moja wersja urządzenia z Androidem to 4.0.4, a android.os.Build.VERSION.SDK_INT to 15
Mam 3 błystki
A także mam klasę Async-Tack.
Oto mój kod ładowania tarczy
To działa idealnie. Jeden po drugim moje błystki załadowane. Nie wykonałem użytkownika executeOnExecutor.
Oto moja klasa zadań asynchronicznych
źródło
jeśli chcesz wykonywać zadania równolegle, musisz wywołać metodę
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")
po wersji Androida 3.0; ale ta metoda nie istnieje przed Androidem 3.0 i po wersji 1.6, ponieważ działa ona równolegle sama, więc sugeruję, abyś dostosował własną klasę AsyncTask w swoim projekcie, aby uniknąć zgłaszania wyjątku w innej wersji Androida.źródło