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 .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
.
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
- lub jakiś wewnętrzny obiekt Java utrzymuje odniesienie do obiektu
AsyncTask
, blokując jego pobieranie śmieci, pozostawiającAsyncTask
do końca w tle
AsyncTask
, ponieważ jedyny obiekt zawierający odniesienie do niego (oryginalny Activity
) zostaje zniszczony
AsyncTask
(lub przekazując ją nowemu Activity
), lub użyj zamiast tego Service
.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.
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