302 votes

Avertissement: Cette classe AsyncTask doit être statique ou des fuites peuvent se produire.

Je reçois un message d'avertissement dans mon code qui prévoit que:

Cette classe AsyncTask doit être statique ou des fuites peuvent se produire (anonyme android.os.AsyncTask)

L'avertissement complet est:

Cette classe AsyncTask doit être statique ou des fuites peuvent se produire (anonyme android.os.AsyncTask) Un champ statique est une fuite de contextes. Non statique intérieure classes ont une référence implicite à leur extérieur de la classe. Si l'extérieur de la classe est, par exemple, un Fragment ou d'une Activité, alors cette mention signifie que le long-running gestionnaire/chargeur/tâche de maintenir une référence à l'activité qui l'empêche de se ordures collectées. De même, le champ direct des références à des activités et des fragments de ces instances en cours d'exécution peut provoquer des fuites. Classes ViewModel ne devrait jamais de point de Vue ou de non-application des Contextes.

C'est mon code:

 new AsyncTask<Void,Void,Void>(){

        @Override
        protected Void doInBackground(Void... params) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });

            return null;
        }
    }.execute();

Comment puis-je corriger cela?

613voto

Suragch Points 197

Comment utiliser un statique à l'intérieur de la classe AsyncTask

Pour éviter les fuites, vous pouvez le faire de l'intérieur de la classe statique. Le problème avec cela, cependant, est que vous n'avez plus accès à l'Activité de l'INTERFACE utilisateur de points de vue ou des variables membres. Vous pouvez passer d'une référence à l' Context mais vous courez le risque d'une fuite de mémoire. (Android ne peut pas collecter les ordures de l'Activité après la fermeture si la classe AsyncTask a une référence forte pour elle.) La solution est de faire une référence faible de l'Activité (ou de ce qu' Context vous avez besoin).

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;
        }
    }
}

Notes

  • Autant que je sache, ce type de fuite de mémoire danger a toujours été vrai, mais j'ai seulement commencé à voir la mise en garde dans Android Studio 3.0. Beaucoup des principaux AsyncTask tutoriaux-il encore ne pas traiter avec elle (voir ici, ici, ici, et ici).
  • Vous aussi suivez une procédure similaire si votre AsyncTask étaient d'un haut niveau de classe. Un statique à l'intérieur de la classe de base est le même comme un haut-niveau de la classe en Java.
  • Si vous n'avez pas besoin de l'Activité elle-même, mais veulent toujours le Contexte (par exemple, pour afficher un Toast), vous pouvez passer une référence pour le contexte de l'application. Dans ce cas, l' AsyncTask constructeur devrait ressembler à ceci:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • Il y a quelques arguments pour ignorer cet avertissement et simplement à l'aide de la non-statique de la classe. Après tout, l'AsyncTask est destiné à être de très courte durée (quelques secondes à la plus longue), et qu'il publiera sa référence à l'Activité lorsqu'il a terminé de toute façon. Voir ce et ce.
  • Excellent article: Comment Fuite Contexte: en effet, les Gestionnaires d' & Inner Classes

Kotlin

Dans Kotlin juste de ne pas inclure l' inner mot-clé à l'intérieur de la classe. De ce fait, il statique par défaut.

Je ne suis pas trop bon à Kotlin encore, afin de corriger mon code ci-dessous si cela peut être amélioré:

class MyActivity : AppCompatActivity() {

    internal var mSomeMemberVariable = 123

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor
        MyTask(this).execute()
    }

    private class MyTask
    internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {

        private val activityReference: WeakReference<MyActivity> = WeakReference(context)

        override fun doInBackground(vararg params: Void): String {

            // do some long running task...

            return "task finished"
        }

        override fun onPostExecute(result: String) {

            // get a reference to the activity if it is still there
            val activity = activityReference.get()
            if (activity == null || activity.isFinishing) return

            // modify the activity's UI
            val textView = activity.findViewById(R.id.textview)
            textView.setText(result)

            // access Activity member variables
            activity.mSomeMemberVariable = 321
        }
    }
}

71voto

Anand Points 73

Les classes internes non statiques contiennent une référence à la classe qui le contient. Lorsque vous déclarez AsyncTask tant que classe interne, elle peut vivre plus longtemps que la classe contenant Activity . Cela est dû à la référence implicite à la classe contenante. Cela empêchera l'activité d'être ramassée, d'où la fuite de mémoire.

Pour résoudre votre problème, utilisez la classe imbriquée statique au lieu de la classe anonyme, locale et interne ou la classe de niveau supérieur.

26voto

Phan Van Linh Points 16963

Cette AsyncTask de la classe doit être statique ou des fuites pourraient se produire en raison

  • Lors de l' Activity est détruit, AsyncTask (les deux static ou non-static) toujours en cours d'exécution
  • Si l'intérieur de la classe est - non-static (AsyncTask) de la classe, il aura de référence à l'extérieur de la classe (Activity).
  • Si un objet n'a pas de point de références elle, Garbage Collected les publierai. Si un objet est utilisé et Garbage Collected ne peut pas le libérer => fuite de mémoire

=> Si AsyncTask est non-static, Activity ne sera pas communiqué les cas, il est détruit => fuite

Solution pour la mise à jour de l'INTERFACE utilisateur après l'AsyncTask comme statique de la classe sans fuite

1) Utiliser WeakReference comme @Suragch répondre
2) d'Envoyer et de supprimer Activity référence à (de) 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);
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X