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ć?

Author: Mike M., 2017-06-01

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.

 31
Author: Anand,
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 konstruktor AsyncTask 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
 323
Author: Suragch,
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ÓWNO static, jak i non-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 i Garbage 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);
        }
    }
}
 12
Author: Phan Van Linh,
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