W moim kodzie pojawia się ostrzeżenie:
Ta klasa AsyncTask powinna być statyczna, w przeciwnym razie mogą wystąpić wycieki (anonimowy android.os.AsyncTask)
Pełne ostrzeżenie to:
Ta klasa AsyncTask powinna być statyczna, w przeciwnym razie mogą wystąpić wycieki (anonimowy android.os.AsyncTask) Pole statyczne spowoduje wyciek kontekstów. Niestatyczne klasy wewnętrzne mają niejawne odniesienie do swojej klasy zewnętrznej. Jeśli ta klasa zewnętrzna jest na przykład Fragmentem lub Działaniem, to odwołanie to oznacza, że długo działający moduł obsługi / moduł ładujący / zadanie będzie zawierał odwołanie do działania, które uniemożliwi gromadzenie śmieci. Podobnie bezpośrednie odniesienia pól do działań i fragmentów z tych dłużej działających instancji mogą powodować wycieki. Klasy ViewModel nigdy nie powinny wskazywać na widoki lub konteksty nieaplikacyjne.
To jest mój kod:
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
return null;
}
}.execute();
Jak to naprawić?
źródło
myActivity.getApplication()
do prywatnego konstruktora dla Singletona, w celu zainicjowania klas RoomDB i innych klas). Moje ViewModels pobierają instancję Singleton jako prywatne odwołanie do wykonywania niektórych operacji na DB. Tak więc ViewModels importuje pakiet Singleton, aandroid.app.Application
nawet jeden z nichandroid.app.Activity
. Ponieważ „Singleton” nie musi importować tych modeli ViewModels do działania, może jednak wystąpić przeciek pamięci?Odpowiedzi:
Niestatyczne klasy wewnętrzne zawierają odniesienie do klasy zawierającej. Gdy deklarujesz
AsyncTask
jako klasę wewnętrzną, może ona żyć dłużej niżActivity
klasa zawierająca . Wynika to z niejawnego odwołania do zawierającej klasy. Zapobiegnie to gromadzeniu śmieci przez działanie, stąd wyciek pamięci.Aby rozwiązać problem, użyj albo statycznej zagnieżdżonej klasy zamiast anonimowej, lokalnej i wewnętrznej lub użyj klasy najwyższego poziomu.
źródło
Jak używać statycznej wewnętrznej klasy AsyncTask
Aby zapobiec wyciekom, możesz ustawić statyczną klasę wewnętrzną. Problem polega na tym, że nie masz już dostępu do widoków interfejsu użytkownika działania lub zmiennych składowych. Możesz przekazać odniesienie do,
Context
ale wtedy ryzykujesz przeciek pamięci. (Android nie może wyrzucić śmieci po zakończeniu działania, jeśli klasa AsyncTask ma do niego silne odniesienie). Rozwiązaniem jest słabe odwołanie do działania (lub cokolwiek,Context
czego potrzebujesz).Notatki
AsyncTask
samouczków wciąż nie radzi sobie z tym (patrz tutaj , tutaj , tutaj i tutaj ).AsyncTask
był na najwyższym poziomie. Statyczna klasa wewnętrzna jest zasadniczo taka sama jak klasa najwyższego poziomu w Javie.Jeśli nie potrzebujesz samego działania, ale nadal chcesz kontekst (na przykład, aby wyświetlić a
Toast
), możesz przekazać odwołanie do kontekstu aplikacji. W tym przypadkuAsyncTask
konstruktor wyglądałby tak:Kotlin
W Kotlin po prostu nie dołączaj
inner
słowa kluczowego dla klasy wewnętrznej. Domyślnie jest to statyczne.źródło
onPostExecute
sposób ponownie w powyższym kodzie. Widać, że zaktualizowałemTextView
tam interfejs użytkownika . Wystarczy użyć,activity.findViewById
aby uzyskać odniesienie do dowolnego elementu interfejsu użytkownika, który należy zaktualizować.activity.isFinishing()
czek i ewentualnie zastąpisz gofragment.isRemoving()
czekiem. Jednak ostatnio nie pracowałem dużo z fragmentami.AsyncTask
konstruktorze przekazujesz odwołanie do swojej klasy zewnętrznej. IdoInBackground()
możesz uzyskać odniesienie do klasy zewnętrznej za pomocąMyOuterClass ref = classReference.get()
. Sprawdź, czy nienull
. (2) WonPostExecute()
aktualizujesz tylko interfejs użytkownika o wyniki z zadania w tle. Podobnie jak w przypadku każdej innej aktualizacji interfejsu użytkownika. Sprawdzenieactivity.isFinishing()
polega na upewnieniu się, że działanie jeszcze się nie zakończyło, w takim przypadku aktualizacja interfejsu użytkownika byłaby bezcelowa.Ta
AsyncTask
klasa powinna być statyczna, ponieważ mogą wystąpić przeciekiActivity
zostanie zniszczonyAsyncTask
(obastatic
lubnon-static
) nadal działająnon-static
(AsyncTask
), będzie miała odwołanie do klasy zewnętrznej (Activity
).Garbage Collected
zwolni go. Jeśli obiekt nie jest używany iGarbage Collected
nie można go zwolnić => pamięć wycieku=> Jeśli
AsyncTask
taknon-static
,Activity
nie zwolni zdarzenia, które zostanie zniszczone => wyciekRozwiązanie do aktualizacji interfejsu użytkownika po ustawieniu AsyncTask jako klasy statycznej bez wycieku
1) Użyj
WeakReference
jak @ Suragch odpowiedź2) Wyślij i usuń
Activity
odniesienie do (z)AsyncTask
źródło
onDestroy()
nie można wezwać za każdym razem