Cytując dokumentację AsyncTask znalezioną tutaj , mówi:
AsyncTasks powinno być idealnie używane do krótkich operacji (najwyżej kilka sekund). Jeśli chcesz, aby wątki działały przez długi czas, zdecydowanie zaleca się użycie różnych interfejsów API dostarczonych przez pakiet java.util.concurrent, takich jak Executor, ThreadPoolExecutor i FutureTask.
Teraz pojawia się moje pytanie: dlaczego? Ta doInBackground()
funkcja działa poza wątkiem interfejsu użytkownika, więc jakie szkody wynikają z długotrwałej operacji w tym miejscu?
android
android-asynctask
user1730789
źródło
źródło
doInBackground
funkcja zawiesza ekran, jeśli pasek postępu nie jest używany.Odpowiedzi:
To bardzo dobre pytanie, jako programista Androida potrzeba czasu, aby w pełni zrozumieć problem. Rzeczywiście, AsyncTask ma dwa główne problemy, które są ze sobą powiązane:
W aplikacji RoboSpice Motivations ( dostępnej w Google Play ) szczegółowo odpowiadamy na to pytanie. Zapewni dogłębny wgląd w AsyncTasks, Loaders, ich funkcje i wady, a także przedstawi alternatywne rozwiązanie dla żądań sieciowych: RoboSpice. Żądania sieciowe są typowym wymaganiem w systemie Android i są z natury długotrwałymi operacjami. Oto fragment aplikacji:
Cykl życia AsyncTask i Activity
AsyncTasks nie podążają za cyklem życia instancji Activity. Jeśli uruchomisz AsyncTask wewnątrz działania i obrócisz urządzenie, działanie zostanie zniszczone i zostanie utworzone nowe wystąpienie. Ale AsyncTask nie umrze. Będzie żyć, dopóki się nie skończy.
Po zakończeniu AsyncTask nie zaktualizuje interfejsu użytkownika nowego działania. Rzeczywiście aktualizuje poprzednie wystąpienie działania, które nie jest już wyświetlane. Może to prowadzić do wyjątku typu java.lang.IllegalArgumentException: Widok niepołączony z menedżerem okien, jeśli na przykład używasz findViewById do pobierania widoku wewnątrz działania.
Problem z wyciekiem pamięci
Tworzenie AsyncTasks jako klas wewnętrznych działań jest bardzo wygodne. Ponieważ AsyncTask będzie musiał manipulować widokami działania, gdy zadanie jest zakończone lub w toku, użycie wewnętrznej klasy działania wydaje się wygodne: klasy wewnętrzne mogą uzyskać bezpośredni dostęp do dowolnego pola klasy zewnętrznej.
Niemniej jednak oznacza to, że klasa wewnętrzna będzie zawierała niewidoczne odniesienie do swojej instancji klasy zewnętrznej: Activity.
W dłuższej perspektywie powoduje to wyciek pamięci: jeśli AsyncTask trwa długo, utrzymuje aktywność „przy życiu”, podczas gdy Android chciałby się go pozbyć, ponieważ nie można go już wyświetlić. Działania nie można zbierać jako śmieci i jest to centralny mechanizm systemu Android służący do ochrony zasobów na urządzeniu.
To naprawdę bardzo zły pomysł, aby używać AsyncTasks do długotrwałych operacji. Niemniej jednak są one dobre dla krótkotrwałych, takich jak aktualizacja widoku po 1 lub 2 sekundach.
Zachęcam do pobrania aplikacji RoboSpice Motivations , która naprawdę wyjaśnia to szczegółowo i zawiera przykłady i demonstracje różnych sposobów wykonywania niektórych operacji w tle.
źródło
Ponieważ
AsyncTask
domyślnie używa puli wątków , której nie utworzyłeś . Nigdy nie wiąż zasobów z puli, której nie utworzyłeś, ponieważ nie wiesz, jakie są wymagania tej puli. I nigdy nie wiąż zasobów z puli, której nie utworzyłeś, jeśli dokumentacja dla tej puli mówi ci, żeby tego nie robić, jak ma to miejsce w tym przypadku.W szczególności, począwszy od Androida 3.2,
AsyncTask
domyślnie używana pula wątków (dla aplikacji zandroid:targetSdkVersion
ustawieniem na 13 lub wyższą) ma tylko jeden wątek - jeśli związasz ten wątek na czas nieokreślony, żadne inne zadania nie zostaną uruchomione.źródło
Service
, po prostu użyj aThread
lub aThreadPoolExecutor
.TimerTask
pochodzi ze standardowej Javy, a nie Androida.TimerTask
został w dużej mierze porzucony na rzeczScheduledExecutorService
(który pomimo swojej nazwy jest częścią standardowej Javy). Żadne z nich nie jest powiązane z Androidem, więc nadal potrzebujesz usługi, jeśli oczekujesz, że te rzeczy będą działać w tle. I naprawdę powinieneś rozważyćAlarmManager
, z Androida, więc nie potrzebujesz usługi kręcącej się po prostu obserwując tykanie zegara.Zadanie Aysnc to wyspecjalizowane wątki, które nadal mają być używane z graficznym interfejsem użytkownika aplikacji, ale jednocześnie zachowują duże zadania wątku interfejsu użytkownika. Więc kiedy takie rzeczy jak aktualizacja list, zmiana widoków itp. Wymagają wykonania pewnych operacji pobierania lub aktualizacji, powinieneś używać zadań asynchronicznych, abyś mógł zachować te operacje poza wątkiem interfejsu użytkownika, ale pamiętaj, że te operacje są nadal w jakiś sposób połączone z interfejsem użytkownika .
W przypadku dłuższych zadań, które nie wymagają aktualizacji interfejsu użytkownika, można zamiast tego korzystać z usług, ponieważ mogą one funkcjonować nawet bez interfejsu użytkownika.
Dlatego w przypadku krótkich zadań używaj zadań asynchronicznych, ponieważ mogą one zostać zabite przez system operacyjny po zaniku aktywności odradzania (zwykle nie umierają w trakcie operacji, ale wykonają swoje zadanie). W przypadku długich i powtarzalnych zadań korzystaj z usług.
aby uzyskać więcej informacji, zobacz wątki:
AsyncTask dłużej niż kilka sekund?
i
AsyncTask nie zatrzyma się nawet po zniszczeniu działania
źródło
Problem z AsyncTask polega na tym, że jeśli jest zdefiniowana jako niestatyczna klasa wewnętrzna działania, będzie miała odniesienie do działania. W scenariuszu, w którym działanie kontenera zadania asynchronicznego kończy się, ale praca w tle w AsyncTask jest kontynuowana, obiekt działania nie będzie zbierany jako śmieci, ponieważ istnieje do niego odwołanie, co powoduje wyciek pamięci.
Rozwiązaniem tego problemu jest zdefiniowanie zadania asynchronicznego jako statycznej wewnętrznej klasy działania i użycie słabego odniesienia do kontekstu.
Mimo to dobrze jest używać go do prostych i szybkich zadań w tle. Aby opracować aplikację z czystym kodem, lepiej jest używać RxJava do uruchamiania złożonych zadań w tle i aktualizowania interfejsu użytkownika za pomocą wyników.
źródło