WeakReference / AsyncTask wzór w Androidzie

[1]} mam pytanie dotyczące tej prostej, często występującej sytuacji w Androidzie .

Mamy główną aktywność, wywołujemy AsyncTask wraz z odniesieniem do mainactivity, tak aby AsyncTask mógł aktualizować widoki na MainActivity.

Podzielę Zdarzenie na etapy

  • MainActivity tworzy AyncTask, przekazuje do niego odniesienie .
  • AysncTask, uruchamia swoją pracę, pobierając na przykład dziesięć plików
  • Użytkownik zmieniono orientację urządzenia. To powoduje, że wskaźnik sierocy w Asynctasku
  • gdy funkcja AsyncTask zakończy działanie i spróbuje uzyskać dostęp do aktywności, aby zaktualizować status, ulega awarii z powodu wskaźnika null .

Rozwiązaniem dla powyższego jest utrzymanie WeakReference w Asynktasku zgodnie z zaleceniem książki "Pro Android 4"

WeakReference<Activity> weakActivity;

in method onPostExecute

Activity activity = weakActivity.get();
if (activity != null) {
   // do your stuff with activity here
}
Jak to rozwiązuje sytuację ?

Moje pytanie, czy mój asynctask ściąga 10 plików, a po zakończeniu 5 aktywność jest restartowana (z powodu zmiany orientacji) to czy moje zadanie pobierania plików zostanie ponownie wywołane ?.

Co stanie się z poprzednią AsyncTask, która została pierwotnie wywołana ?

Dziękuję i przepraszam za Długość pytania .
Author: Muhammad Ahmed AbuTalib, 2013-08-18

3 answers

Jak to rozwiązuje sytuację ?

WeakReference pozwala Activity na zbieranie śmieci, więc nie masz wycieku pamięci.

Odniesienie null oznacza, że AsyncTask Nie można na ślepo aktualizować interfejsu użytkownika, który nie jest już dołączony, co spowoduje wyrzucenie WYJĄTKÓW (np. widok nie jest dołączony do menedżera okien). Oczywiście musisz sprawdzić, czy nie ma null, aby uniknąć NPE.

Jeśli mój asynctask pobiera 10 plików, a po zakończenie 5 aktywność jest restartowana (z powodu zmiany orientacji) to czy moje zadanie pobierania plików zostanie ponownie wywołane ?.

Zależy od twojej implementacji, ale prawdopodobnie tak - jeśli celowo nie robisz czegoś, aby ponowne pobieranie było niepotrzebne, np. buforowanie wyników gdzieś.

Co stanie się z poprzednim AsyncTask, który został pierwotnie wywołany ?

We wcześniejszych wersjach Androida będzie działać do zakończenia, pobieranie wszystkich plików tylko po to, aby je wyrzucić (lub być może buforować, w zależności od implementacji).

W nowszych androidach jestem podejrzany, że AsyncTask'S są zabijane wraz z Activity, który je rozpoczął, ale Moja podstawa podejrzenia jest tylko to, że demo wycieku pamięci dla RoboSpice (patrz poniżej) nie wyciekają na moich JellyBean urządzeń.

Jeśli mogę coś doradzić: AsyncTask nie nadaje się do wykonywania potencjalnie długich zadań, takich jak networking.

IntentService jest lepszym (i wciąż stosunkowo prostym) podejściem, jeśli pojedynczy wątek roboczy jest dla Ciebie akceptowalny. Użyj (local) Service jeśli chcesz mieć kontrolę nad pulą wątków - i uważaj, aby nie pracować nad głównym wątkiem!

RoboSpice wydaje się dobry, jeśli szukasz sposobu na niezawodne wykonywanie sieci w tle(zastrzeżenie: nie próbowałem; nie jestem powiązany). W Sklepie play znajduje się aplikacja demonstracyjna RoboSpice Motivations co wyjaśnia dlaczego powinieneś go użyć, pokazując wszystkie rzeczy, które mogą pójść nie tak z AsyncTask - w tym obejście problemu WeakReference.

Zobacz także ten wątek: czy AsyncTask jest naprawdę wadliwy koncepcyjnie, czy po prostu czegoś mi brakuje?

Aktualizacja:

Stworzyłem projekt github Z przykładem pobierania za pomocą {[7] } dla innego więc pytanie (jak naprawić Androida.os.NetworkOnMainThreadException?), ale jest to również istotne chyba tutaj. Dodatkową zaletą jest to, że zwracając wynik przez onActivityResult, pobranie, które jest w locie po obróceniu urządzenia, dostarczy do zrestartowanego Activity.

 33
Author: Stevie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:12

Klasa WeakReference zasadniczo uniemożliwia JRE zwiększenie licznika referencji dla danej instancji.

Nie wejdę do zarządzania pamięcią Javy i nie odpowiem bezpośrednio na twoje pytanie: WeakReference rozwiązuje sytuację, dostarczając AsyncTask sposób, aby dowiedzieć się, czy jego aktywność rodzica jest nadal ważna.

Sama zmiana orientacji nie spowoduje automatycznego ponownego uruchomienia AsyncTask. Musisz zakodować pożądane zachowanie za pomocą znanych mechanizmów (onCreate/onDestroy, onSave/RestoreInstanceState).

Jeśli chodzi o oryginał AsyncTask, nie jestem w 100 % pewien, która z tych opcji się wydarzy:

    [2]} Java zatrzymuje wątek i usuwa AsyncTask, ponieważ jedyny obiekt zawierający odniesienie do niego (oryginalny Activity) zostaje zniszczony
  • lub jakiś wewnętrzny obiekt Java utrzymuje odniesienie do obiektu AsyncTask, blokując jego pobieranie śmieci, pozostawiając AsyncTask do końca w tle
Tak czy siak, dobrą praktyką byłoby przerwij/Wstrzymaj i uruchom ponownie / Wznów ręcznie AsyncTask (lub przekazując ją nowemu Activity), lub użyj zamiast tego Service.
 7
Author: domsom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-18 20:50:45

Jak to rozwiązuje sytuację ?

Nie ma.

Referent WeakReference jest ustawiony na null, gdy moduł garbage collector stwierdzi, że referent jest słabo osiągalny. Nie dzieje się tak, gdy działanie jest wstrzymane i niekoniecznie dzieje się to natychmiast, gdy działanie jest zniszczone, a framework odrzuca wszystkie odniesienia do niego. Jeśli GC nie działa, jest całkowicie możliwe, aby AsyncTask zakończył się, gdy jego WeakReference nadal zawiera odniesienie do martwej aktywności.

Nie tylko to, ale takie podejście nie zapobiega niepotrzebnemu zużywaniu procesora.

Lepszym podejściem jest utrzymywanie Activity silnego odniesienia do AsyncTask i cancel(...) it w odpowiedniej metodzie cyklu życia. AsyncTask powinien monitorować isCancelled() i przestać działać, jeśli nie jest już potrzebna.

Jeśli chcesz, aby AsyncTask przetrwał przez zmiany konfiguracji (ale Nie Inne formy niszczenia aktywności) można go hostować w zachowanym fragmencie.

 1
Author: Kevin Krumwiede,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-24 06:09:04