Ostrzeżenie: ta klasa AsyncTask powinna być statyczna lub mogą wystąpić wycieki
Otrzymuję ostrzeżenie w moim kodzie, które stwierdza:
Ta klasa AsyncTask powinna być statyczna lub mogą wystąpić wycieki (anonimowy android.os.AsyncTask)
Pełne Ostrzeżenie To:
Ta klasa AsyncTask powinna być statyczna lub mogą wystąpić wycieki (anonimowy android.os.AsyncTask) Pole statyczne spowoduje wyciek kontekstów. Niestatyczne klasy wewnętrzne mają ukryte odniesienie do swojej klasy zewnętrznej. Jeśli ta zewnętrzna klasa jest na przykład fragmentem lub aktywnością, to to odniesienie oznacza, że długo działający handler / loader / task będzie zawierał odniesienie do działania, które zapobiega zbieraniu śmieci. Podobnie bezpośrednie odniesienia w terenie do działań i fragmentów z tych dłuższych wystąpień mogą powodować wycieki. Klasy ViewModel nigdy nie powinny wskazywać widoków ani kontekstów innych niż aplikacje.
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 poprawić?
3 answers
Niestatyczne klasy wewnętrzne zawierają odniesienie do klasy zawierającej. Kiedy zadeklarujesz AsyncTask
jako klasę wewnętrzną, może ona żyć dłużej niż klasa zawierająca Activity
. Wynika to z niejawnego odniesienia do klasy zawierającej. Zapobiegnie to zbieraniu śmieci, a tym samym wyciekowi pamięci.
Aby rozwiązać problem, użyj klasy zagnieżdżonej statycznie zamiast klasy anonimowej, lokalnej i wewnętrznej lub użyj klasy najwyższego poziomu.
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-06-01 13:58:50
Jak używać statycznej wewnętrznej klasy AsyncTask
Aby zapobiec wyciekom, możesz uczynić klasę wewnętrzną statyczną. Problem z tym polega jednak na tym, że nie masz już dostępu do widoków interfejsu użytkownika lub zmiennych członkowskich aktywności. Możesz przejść w referencji do Context
, ale wtedy narażasz się na takie samo ryzyko wycieku pamięci. (Android nie może zbierać śmieci po zamknięciu, jeśli Klasa AsyncTask ma silne odniesienie do niej.) Rozwiązaniem jest słabe odniesienie do działalności (lub cokolwiek potrzebujesz).
public class MyActivity extends AppCompatActivity {
int mSomeMemberVariable = 123;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// start the AsyncTask, passing the Activity context
// in to a custom constructor
new MyTask(this).execute();
}
private static class MyTask extends AsyncTask<Void, Void, String> {
private WeakReference<MyActivity> activityReference;
// only retain a weak reference to the activity
MyTask(MyActivity context) {
activityReference = new WeakReference<>(context);
}
@Override
protected String doInBackground(Void... params) {
// do some long running task...
return "task finished";
}
@Override
protected void onPostExecute(String result) {
// get a reference to the activity if it is still there
MyActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
// modify the activity's UI
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
// access Activity member variables
activity.mSomeMemberVariable = 321;
}
}
}
Uwagi
- z tego co wiem, ten rodzaj zagrożenia wyciekiem pamięci zawsze był prawdziwy, ale dopiero zacząłem widzieć ostrzeżenie w Android Studio 3.0. Wiele głównych
AsyncTask
samouczków nadal nie radzi sobie z tym (Zobacz tutaj, proszę., tutaj i tutaj ).
Podobnie postąpiłbyś, gdyby twoja klasa była na najwyższym poziomie. Statyczna Klasa wewnętrzna jest w zasadzie taka sama jak klasa najwyższego poziomu w Javie.
-
Jeśli nie potrzebujesz samej aktywności, ale nadal chcesz mieć kontekst (na przykład, aby wyświetlić
Toast
), możesz przekazać odniesienie do kontekstu aplikacji. W tym przypadku konstruktorAsyncTask
wyglądałby tak:private WeakReference<Application> appReference; MyTask(Application context) { appReference = new WeakReference<>(context); }
- istnieje kilka argumentów za ignorowaniem tego ostrzeżenia i używaniem klasy non-static. W końcu AsyncTask ma być bardzo krótkotrwały( najdłuższy o kilka sekund) i wyda swój odniesienie do aktywności, gdy i tak się skończy. Zobacz to i to .
- doskonały artykuł: Jak wyciekać kontekst: Handlers & Inner Classes
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
2018-01-26 16:53:45
Ta klasa AsyncTask
powinna być statyczna lub może wystąpić wyciek, ponieważ
- gdy
Activity
zostanie zniszczony,AsyncTask
(ZARÓWNOstatic
, jak inon-static
) nadal działa - Jeśli Klasa wewnętrzna jest
non-static
(AsyncTask
) klasa, będzie miała odniesienie do klasy zewnętrznej (Activity
). - jeśli obiekt nie ma odniesienia do niego,
Garbage Collected
go zwolni. Jeśli obiekt jest nieużywany iGarbage Collected
nie można zwolnić go = > pamięć wycieku
=> If AsyncTask
is non-static
, Activity
NIE WYDA zdarzenia is destroyed = > leak
Rozwiązanie do aktualizacji interfejsu użytkownika po utworzeniu AsyncTask jako klasy statycznej bez wycieku
1) Użyj WeakReference
Jak @Suragch odpowiedz
2) Wyślij i usuń Activity
odniesienie do (from) AsyncTask
public class NoLeakAsyncTaskActivity extends AppCompatActivity {
private ExampleAsyncTask asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// START AsyncTask
asyncTask = new ExampleAsyncTask();
asyncTask.setListener(new ExampleAsyncTask.ExampleAsyncTaskListener() {
@Override
public void onExampleAsyncTaskFinished(Integer value) {
// update UI in Activity here
}
});
asyncTask.execute();
}
@Override
protected void onDestroy() {
asyncTask.setListener(null); // PREVENT LEAK AFTER ACTIVITY DESTROYED
super.onDestroy();
}
static class ExampleAsyncTask extends AsyncTask<Void, Void, Integer> {
private ExampleAsyncTaskListener listener;
@Override
protected Integer doInBackground(Void... voids) {
...
return null;
}
@Override
protected void onPostExecute(Integer value) {
super.onPostExecute(value);
if (listener != null) {
listener.onExampleAsyncTaskFinished(value);
}
}
public void setListener(ExampleAsyncTaskListener listener) {
this.listener = listener;
}
public interface ExampleAsyncTaskListener {
void onExampleAsyncTaskFinished(Integer value);
}
}
}
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
2018-04-10 13:19:11