AsyncTask
wykonywanie złożonych zadań w innym wątku to świetna sprawa.
Ale gdy nastąpi zmiana orientacji lub inna zmiana konfiguracji, gdy AsyncTask
nadal działa, prąd Activity
jest niszczony i restartowany. Ponieważ instancja programu AsyncTask
jest połączona z tym działaniem, kończy się niepowodzeniem i powoduje wyświetlenie okna komunikatu „wymuś zamknięcie”.
Dlatego szukam jakiejś „najlepszej praktyki”, aby uniknąć tych błędów i zapobiec niepowodzeniu AsyncTask.
Do tej pory widziałem:
- Wyłącz zmiany orientacji (na pewno nie jest to sposób, w jaki powinieneś sobie z tym poradzić).
- Pozostawienie zadania na przetrwanie i zaktualizowanie go o nową instancję działania za pośrednictwem
onRetainNonConfigurationInstance
- Po prostu anuluj zadanie, gdy
Activity
zostanie zniszczone i uruchom je ponownie, gdyActivity
zostanie ponownie utworzone. - Powiązanie zadania z klasą aplikacji zamiast z instancją działania.
- Jakaś metoda używana w projekcie „półki” (przez onRestoreInstanceState)
Niektóre przykłady kodu:
Android AsyncTasks podczas obracania ekranu, część I i część II
Czy możesz mi pomóc znaleźć najlepsze podejście, które najlepiej rozwiązuje problem i jest łatwe do wdrożenia? Sam kod jest również ważny, ponieważ nie wiem, jak to poprawnie rozwiązać.
źródło
Odpowiedzi:
Czy nie używać
android:configChanges
w celu rozwiązania tego problemu. To bardzo zła praktyka.Czy nie używać
Activity#onRetainNonConfigurationInstance()
albo. Jest to mniej modułowe i nie nadaje się doFragment
aplikacji opartych na podstawach.Możesz przeczytać mój artykuł opisujący, jak radzić sobie ze zmianami konfiguracji przy użyciu zachowanych plików
Fragment
s.AsyncTask
Ładnie rozwiązuje problem utrzymywania zmiany w poprzek obrotu. Zasadniczo musisz hostować swojeAsyncTask
wnętrzeFragment
, dzwonićsetRetainInstance(true)
do niegoFragment
i zgłaszaćAsyncTask
postępy / wyniki z powrotem do niegoActivity
przez zachowaneFragment
.źródło
TabActivity
. Szczerze mówiąc, nie jestem pewien, dlaczego w ogóle o tym rozmawiamy ... wszyscy zgadzają się, żeFragment
jest to właściwy kierunek. :)Zwykle rozwiązuję ten problem, mając intencje emisji ognia AsyncTasks w wywołaniu zwrotnym .onPostExecute (), więc nie modyfikują one działania, które je bezpośrednio uruchomiło. Aktywności słuchają tych transmisji z dynamicznymi odbiornikami BroadcastReceivers i odpowiednio reagują.
W ten sposób AsyncTasks nie muszą przejmować się konkretną instancją Activity, która obsługuje ich wynik. Po prostu „krzyczą”, kiedy skończą, a jeśli jakieś działanie jest w tym czasie (jest aktywne i skoncentrowane / jest w stanie wznowienia), które jest zainteresowane wynikami zadania, zostanie ono obsłużone.
Wiąże się to z nieco większym obciążeniem, ponieważ środowisko wykonawcze musi obsługiwać transmisję, ale zwykle nie mam nic przeciwko. Myślę, że użycie LocalBroadcastManager zamiast domyślnego systemu w całym systemie nieco przyspiesza.
źródło
Oto kolejny przykład AsyncTask, który używa a
Fragment
do obsługi zmian konfiguracji środowiska uruchomieniowego (na przykład, gdy użytkownik obraca ekran) zsetRetainInstance(true)
. Pokazany jest również określony (regularnie aktualizowany) pasek postępu.Przykład jest częściowo oparty na oficjalnej dokumentacji Zachowanie obiektu podczas zmiany konfiguracji .
W tym przykładzie praca wymagająca wątku w tle polega na samym załadowaniu obrazu z Internetu do interfejsu użytkownika.
Wydaje się, że Alex Lockwood ma rację, że jeśli chodzi o obsługę zmian konfiguracji środowiska wykonawczego za pomocą AsyncTasks, najlepszym rozwiązaniem jest użycie „Zachowanego fragmentu”.
onRetainNonConfigurationInstance()
zostaje uznany za przestarzały w Lint w Android Studio. Oficjalna dokumentacja ostrzega nas przed używaniemandroid:configChanges
, od samodzielnej obsługi zmiany konfiguracji , ...Następnie pojawia się kwestia, czy w ogóle należy używać AsyncTask dla wątku w tle.
Oficjalny wniosek o AsyncTask ostrzega ...
Alternatywnie można użyć usługi, programu ładującego (przy użyciu CursorLoader lub AsyncTaskLoader) lub dostawcy treści do wykonywania operacji asynchronicznych.
Resztę postu dzielę na:
Procedura
Zacznij od podstawowego AsyncTask jako wewnętrznej klasy działania (nie musi to być klasa wewnętrzna, ale prawdopodobnie będzie to wygodne). Na tym etapie AsyncTask nie obsługuje zmian konfiguracji środowiska wykonawczego.
Dodaj zagnieżdżoną klasę RetainedFragment, która rozszerza klasę Fragement i nie ma własnego interfejsu użytkownika. Dodaj setRetainInstance (true) do zdarzenia onCreate tego fragmentu. Zapewnij procedury ustawiania i pobierania danych.
W najbardziej zewnętrznej klasie Activity, onCreate () obsługuje RetainedFragment: Odwołaj się do niego, jeśli już istnieje (w przypadku ponownego uruchamiania działania); utwórz i dodaj, jeśli nie istnieje; Następnie, jeśli już istniał, pobierz dane z RetainedFragment i ustaw interfejs użytkownika przy użyciu tych danych.
Zainicjuj AsyncTask z interfejsu użytkownika
Dodaj i zakoduj określony pasek postępu:
Cały kod powyższej procedury
Układ zajęć.
Działanie z: podklasą klasy wewnętrznej AsyncTask; podklasa klasy wewnętrznej RetainedFragment, która obsługuje zmiany konfiguracji środowiska wykonawczego (np. gdy użytkownik obraca ekran); i określony pasek postępu aktualizowany w regularnych odstępach czasu. ...
W tym przykładzie funkcja biblioteki (wymieniona powyżej z wyraźnym prefiksem pakietu com.example.standardapplibrary.android.Network), która naprawdę działa ...
Dodaj wszelkie uprawnienia wymagane przez zadanie w tle do pliku AndroidManifest.xml ...
Dodaj swoją aktywność do AndroidManifest.xml ...
źródło
Niedawno znalazłem rozwiązanie dobre tutaj . Opiera się na zapisaniu obiektu zadania za pośrednictwem RetainConfiguration. Z mojego punktu widzenia rozwiązanie jest bardzo eleganckie i jak dla mnie zacząłem z niego korzystać. Wystarczy zagnieździć swoje zadanie asynchroniczne z zadania podstawowego i to wszystko.
źródło
Na podstawie odpowiedzi @Alex Lockwood i odpowiedzi @William & @quickdraw mcgraw w tym poście: Jak obsługiwać wiadomości obsługi, gdy aktywność / fragment jest wstrzymany , napisałem ogólne rozwiązanie.
W ten sposób rotacja jest obsługiwana, a jeśli aktywność przejdzie w tło podczas wykonywania zadania asynchronicznego, po wznowieniu działanie otrzyma wywołania zwrotne (onPreExecute, onProgressUpdate, onPostExecute i onCancelled), więc nie zostanie zgłoszony wyjątek IllegalStateException (zobacz Jak obsługiwać Handler wiadomości, gdy aktywność / fragment jest wstrzymany ).
Byłoby wspaniale mieć to samo, ale z ogólnymi typami argumentów, jak AsyncTask (np: AsyncTaskFragment <Params, Progress, Result>), ale nie udało mi się to zrobić szybko i nie mam w tej chwili czasu. Jeśli ktoś chce coś ulepszyć, nie krępuj się!
Kod:
Będziesz potrzebować PauseHandler:
Przykładowe użycie:
źródło
Możesz do tego użyć Loaderów. Sprawdź Doc tutaj
źródło
Dla tych, którzy chcą unikać fragmentów, możesz zachować AsyncTask działający przy zmianach orientacji za pomocą onRetainCustomNonConfigurationInstance () i okablowania.
(Zwróć uwagę, że ta metoda jest alternatywą dla przestarzałej metody onRetainNonConfigurationInstance () ).
Wydaje się, że to rozwiązanie nie jest jednak często wymieniane. Aby zilustrować, napisałem prosty przykład działania.
Twoje zdrowie!
źródło
Wdrożyłem bibliotekę, która może rozwiązać problemy z wstrzymywaniem aktywności i odtwarzaniem podczas wykonywania zadania.
Powinieneś wdrożyć
AsmykPleaseWaitTask
iAsmykBasicPleaseWaitActivity
. Twoja aktywność i zadanie w tle będą działać dobrze, nawet jeśli będziesz obracać ekran i przełączać się między aplikacjamiźródło
SZYBKIE OBEJŚCIE (niezalecane)
Aby uniknąć zniszczenia i utworzenia działania, należy zadeklarować swoją aktywność w pliku manifestu: android: configChanges = "orientacja | keyboardHidden | screenSize
Jak wspomniano w dokumentach
źródło