[JAVA] Avertissement à la classe imbriquée d'AsyncTask définie dans l'activité "Cette classe'AsyncTask 'doit être statique ou des fuites peuvent se produire"

Cet article Ceci est une suite du message intitulé "Référence implicite des objets englobants détenus par des classes imbriquées non statiques". Par conséquent, je vous serais reconnaissant si vous pouviez y jeter un coup d'œil en premier. Cet article est, pour le dire franchement, "Ajouter le modificateur static à la classe imbriquée".

J'ai essayé de créer une application qui commence soudainement à compter ...

Dès que vous le démarrez, le décompte commencera par incréments d'une seconde. Si vous partez de START!, Comptez jusqu'à "9" et vous avez terminé. (Ce GIF animé est une animation qui se répète START! Quand il atteint 3, mais je suis désolé car il n'y a aucun moyen d'arrêter le GIF animé collé sur cette Qiita)

countup.gif

L'environnement est le suivant.

Android Studio 3.6.1 Build #AI-192.7142.36.36.6241897, built on February 27, 2020 Runtime version: 1.8.0_212-release-1586-b04 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o Windows 10 10.0 GC: ParNew, ConcurrentMarkSweep Memory: 1237M Cores: 8 Registry: ide.new.welcome.screen.force=true Non-Bundled Plugins:

Non développé à Kotlin. Je vais le faire en Java.

Je reçois un avertissement sur le code d'activité

L'activité de cette application est la suivante.

MainActivity.java


public class MainActivity extends AppCompatActivity {

    private TextView textView;

    private MyTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.text_view);
        task = new MyTask();
        task.execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        task.cancel(true);
    }

    class MyTask extends AsyncTask<Void, String, Void> {
        @Override
        protected Void doInBackground(Void... voids) {
            for (int i = 0; i < 10; i++) {                if (isCancelled()) {
                    return null; // break;Mais peut-être
                }
                try {
                    Thread.sleep(1000);
                    publishProgress(String.valueOf(i));
                } catch (InterruptedException e) {
                    Log.e("MyTask", e.getMessage(), e);
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            textView.setText(values[0]);
        }
    }
}

Ensuite, Android Studio donne cet avertissement.

thisasynctaskclassshouldbestaticorleaksmightoccur.png

This 'AsyncTask' class should be static or leaks might occur

Je blâme la classe imbriquée nommée MyTask pour ne pas avoir le modificateur static.

Le but de cet article est d'éviter d'émettre cet avertissement.

Cela semble être la meilleure solution (grand public)

Tout d'abord, je posterai le code de la conclusion. Nous décidons de conserver la ** référence faible ** de l'objet de classe englobante MainActicity dans l'objet de classe imbriqué statique. Alors, utilisez java.lang.ref.WeakReference.

La meilleure solution


public class MainActivityGood extends AppCompatActivity {

    //Arrêtez le champ TextView.

    private MyTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.text_view);
        //Passez-vous ça? Pourquoi? C'est parce que j'ai un constructeur dans MyTask.
        task = new MyTask(this);
        task.execute(textView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        task.cancel(true);
    }

    static class MyTask extends AsyncTask<Void, String, Void> {

        WeakReference<Activity> activityReference; //Attention ici!

        //J'ai un constructeur.
        public MyTask(Activity activity) {
            //Nous conserverons l'objet de la classe englobante avec une référence faible.
            activityReference= new WeakReference<>(activity);
        }

        @Override
        protected Void doInBackground(Void... voids) {
            for (int i = 0; i < 10; i++) {                if (isCancelled()) {
                    return null; // break;Mais peut-être
                }
                try {
                    Thread.sleep(1000);
                    publishProgress(String.valueOf(i));
                } catch (InterruptedException e) {
                    Log.e("MyTask", e.getMessage(), e);
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            //De faibles références aux objets de la classe englobante peuvent être obtenues avec la méthode get.
            Activity activity = activityReference.get();
            //Et vérifions si la référence faible obtenue est nulle.
            if (activity == null || activity.isFinishing()) {
                return; // ※
            }

            TextView textView = activity.findViewById(R.id.text_view);
            textView.setText(values[0]);
        }
    }
}

La conclusion est comme ça, mais comme c'est un gros problème, j'aimerais avoir un grand cercle à la fin pendant que je suis sur le point d'arriver à cette conclusion.

Ajouter un modificateur statique à la classe imbriquée

Comme l'a dit l'avertissement d'Android Studio, j'ai ajouté aveuglément (rires) le modificateur static à la sous-classe ʻAsyncTask nommée MyTask`.

Cependant, si vous prenez de telles mesures, vous vous fâcherez en accédant au champ non statique de la classe englobante, comme indiqué dans l'image ci-dessous. Pourquoi. En effet, les classes imbriquées statiques ne peuvent accéder qu'aux membres statiques du composant.

Non-static field.png

Puis

N'est-il pas bon de modifier MainActivity comme ça? Avec une sensation de facilité


public class MainActivity extends AppCompatActivity {

    private static TextView textView;

    //Ce qui suit est omis
}

Non, n'est-ce pas un peu différent? Je te blâme.

J'ai écrit un code terrible

Cela semble intentionnel, mais jetez un œil. Mais c'est pourquoi l'avertissement d'exemple d'Android Studio disparaît.

MainActivityDasai.java


public class MainActivityDasai extends AppCompatActivity {

    private MyTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView = findViewById(R.id.text_view);
        task = new MyTask();
        task.execute(textView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        task.cancel(true);
    }

    static class MyTask extends AsyncTask<TextView, Object, Void> {
        @Override
        protected Void doInBackground(TextView... textViews) {
            for (int i = 0; i < 100000; i++) {
                if (isCancelled()) {
                    return null; // break;Mais peut-être
                }
                try {
                    Thread.sleep(1000);
                    publishProgress(textViews[0], String.valueOf(i));
                } catch (InterruptedException e) {
                    Log.e("MyTask", e.getMessage(), e);
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Object... values) {
            ((TextView)values[0]).setText((String)values[1]);
        }
    }
}

Ma patrouille égare mon corps et mon âme.

--Ce MyTask est une classe qui n'est utilisée que dans cette MainActivity, donc je veux en faire une classe imbriquée.

Cette exploration est venue à l'idée que "* Alors, arrêtons de faire de TextView un champ de la classe englobante! *", Et c'est devenu un programme tellement merdique.

Alors je voulais me reprocher d'avoir écrit ce programme merdique. Ce que je n'aime pas, c'est que ** j'en ai assez de spécifier ʻObject pour les génériques **. Grâce à cela, la partie de " ((TextView) values [0]). SetText ((String) values [1]); " est castée, et quel est l'index du tableau? Prête à confusion. De plus, je n'aime pas le fait que l'argument de la méthode doInBackground et le premier générique de ʻAsyncTask soient TextView. Que faire si le nombre de vues autres que «TextView» augmente?

Utilisez WeakReference pour traiter l'activité reçue comme une «référence faible»

J'ai décidé d'utiliser java.lang.ref.WeakReference et de le traiter comme une ** référence faible **. Veuillez consulter MainActivityGood.java ci-dessus.

Le point est

--Définissez un constructeur pour MyTask, qui est une classe imbriquée statique. --Définissez également un champ de type WeakReference avec la classe englobante spécifiée dans les génériques. --Et faites-le "nouveau" en utilisant les arguments du constructeur. Cela garde une référence faible pour MainActivityGood!

Contrairement à la manière merdique ci-dessus, cela facilite le traitement des vues autres que TextView, et vous n'avez pas à faire la chose stupide de spécifier ʻObject` dans les génériques.

c'est tout.

référence

Ceci est similaire à l'article de Qiita "Cette classe Handler devrait être statique ou des fuites pourraient se produire".

AsyncTask est obsolète depuis le niveau d'API R

ʻAndroid.os.AsyncTask` est obsolète depuis le niveau d'API R [^ 1]. Cet article est également voué à être obsolète ...

[^ 1]: Bien que le niveau d'API soit indiqué par un nombre, il est "R" au moment de la rédaction de cet article.

L'alternative est

--Utilisez le package java.util.concurrent comme standard ou -Utilisez les utilitaires de concurrence Kotlin.

... Apparemment ... Ce dernier est, par essence, les Coroutines de Kotlin.

Recommended Posts

Avertissement à la classe imbriquée d'AsyncTask définie dans l'activité "Cette classe'AsyncTask 'doit être statique ou des fuites peuvent se produire"
SpringBoot 2.3.0 ou version ultérieure ne parvient pas à résoudre l'espace réservé utilisé pour la valeur de @PropertySource
[Rails] Où faire attention dans la description de la validation