Cet article décrit «Soyez prudent, car les classes imbriquées non statiques saisissent l'objet englobant comme une référence implicite» dans Java.
La raison pour laquelle vous devez faire attention est que cela augmente la possibilité de provoquer une fuite de mémoire.
Et la ligne du bas est: "Les classes imbriquées sont mieux avec le modificateur static
(car elles ne contiennent pas l'objet englobant comme référence implicite)." Les objets englobants sont de courte durée, surtout si les instances de classe imbriquées ont une durée de vie plus longue!
(Cet article n'est pas spécifique au développement d'applications Android, mais au fur et à mesure que vous le lirez, vous serez impliqué dans Java, alors soyez patient au début.)
C'était un jour. Lorsque je créais une application Android en Java, Android Studio [^ 1] a émis un avertissement comme celui-ci.
[^ 1]: IDE pour le développement d'applications Android réalisé en modifiant IntelliJ IDEA
This 'AsyncTask' class should be static or leaks might occur
Cette classe, nommée CouponAsyncTask, est définie comme une classe imbriquée (classe interne / classe interne), mais l'info-bulle dit: «Ajoutez un modificateur statique, sinon cela peut provoquer une fuite de mémoire». Je suis. Eh bien, j'ai peur.
Si vous regardez l'explication détaillée de la fonction de vérification de code "Lint" d'Android Studio,
Static Field Leaks A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts. Issue id: StaticFieldLeak
Je vais demander la traduction à Google.
Fuite de champ statique Contexte de fuite des champs statiques. Les classes internes non statiques ont des références implicites aux classes externes. Si la classe externe est, par exemple, un fragment ou une activité, cette référence signifie que le gestionnaire / chargeur / tâche de longue durée contient une référence à l'activité qui l'empêche de récupérer le garbage collection. De même, les références de champ directes aux activités et aux fragments de ces instances plus longues peuvent provoquer des fuites. La classe ViewModel ne pointe pas vers un contexte de vue ou de non-application. ID du problème: StaticFieldLeak
Même si une phrase anglaise difficile est traduite en un japonais difficile ... C'est Chimpung Kampung.
Il a été correctement publié sur le site officiel des développeurs d'applications Android comme "un exemple de faille courante dans la conception de code utilisant des objets filetés". Je me demande s'il y en a. Amélioration des performances par threading [référence implicite]
La page japonaise de ce site officiel se traduit par "références implicites", mais le texte original est écrit par "références implicites". Dans cet article, nous utiliserons des mots composés de cinq caractères comme «référence implicite».
Même si je parle soudain de "référence implicite d'objet englobant détenu par une classe imbriquée non statique", il y a beaucoup d'informations sur ce sujet, donc j'expliquerai les connaissances préalables une par une. Dépêchez-vous. Cependant, cela peut être un peu frustrant.
Une «classe imbriquée» est également appelée «classe interne». Cet article est unifié avec "classe imbriquée".
La classe Foo a deux classes imbriquées
public class Foo {
class Bar {
void methodBar() {
System.out.println("methodBar");
}
}
static class Baz {
void methodBaz() {
System.out.println("methodBaz");
}
}
void methodFoo() {
System.out.println("methodFoo");
}
}
dans ce cas,
La «classe englobante» est également connue sous le nom de «classe externe». Cet article sera unifié avec «Classe englobante».
La différence entre Bar et Baz est de savoir s'ils ont ou non le modificateur static
.
Créer deux classes imbriquées de Foo
class Main {
public static void main(String[] args) {
Foo foo = new Foo();
Foo.Bar bar = foo.new Bar();
Foo.Baz baz = new Foo.Baz();
}
}
Il a fallu deux lignes pour créer une instance de la classe Bar. Si vous voulez vraiment le faire en une seule ligne, utilisez le code suivant.
Créer une instance de la classe Bar sur une seule ligne
Foo.Bar bar = new Foo().new Bar();
Dans tous les cas, si vous souhaitez créer une instance d'une classe imbriquée non statique, vous avez besoin d'une instance de clôture. Et il est également caractéristique que «.» Soit ajouté avant l'opérateur «nouveau».
Vous n'avez pas besoin d'une instance englobante à l'avance. Vous pouvez soudainement «nouveau».
Ce qui est unique, c'est que le nom de la classe est " Foo.Baz
"au lieu de" Baz
". Il est caractéristique que «.» Soit inclus entre les noms de classe.
Effective java a publié "Static member classes plutôt que non-static member classes" à partir de la première édition (publiée en 2001). J'ai beaucoup de choses à choisir. C'est une cérémonie de réussite ou d'échec qui reste même dans la troisième édition publiée en 2018.
Les fuites de mémoire peuvent être catastrophiques.
Et
Les fuites de mémoire sont généralement difficiles à détecter.
Et
Conservez des références supplémentaires et perdez de la mémoire et du temps.
Par exemple, des choses effrayantes sont écrites. Parce que
** Si vous omettez le modificateur statique, chaque instance aura une référence non pertinente à l'objet englobant. La sauvegarde de cette référence prend du temps et de la mémoire. La chose sérieuse est que sans cette référence, l'instance englobante peut rester si elle est soumise au garbage collection. ** **
Est écrit.
J'ai un peu modifié le programme ci-dessus. La classe imbriquée statique Baz
a été un peu rejetée.
python
public class Main {
public static void main(String[] args) {
Foo foo = new Foo();
Foo.Bar bar = foo.new Bar();
foo = null;
System.gc();
// foo.methodFoo(); //NullPointerException s'est produite
bar.methodBar();
}
}
J'ai assigné null
à l'instance Foo
de la classe englobante pour effectuer le garbage collection (GC), puis j'ai appelé la méthode à l'instance Bar
de la classe imbriquée non statique. Quand j'essaye de l'exécuter, c'est comme suit.
Résultat d'exécution
methodBar
Ouais, ça marche! Je l'ai regardé.
Selon la spécification Java, peu importe combien j'attribue null
à l'instance Foo
de la classe englobante, tant que l'instance Bar
de la classe imbriquée non statique fonctionne, l'objet nommé foo
sera Ce n'est pas GC (il a été brillamment passé sans être considéré comme une cible GC). Parce que ** bar
contient foo
comme référence implicite **.
Si vous faites une illustration par analogie amusante, cela ressemble à ceci.
La femme qui monte les escaliers pas à pas est pleine d'énergie au «bar».
D'un autre côté, le vieil homme qui veut devenir Bouddha est «foo», mais parce que «bar» le tient, il n'est pas facilement appelé au paradis. Cela peut être un esprit terrestre.
--Gripping = référence
Veuillez lire comme.
Arrêtez d'utiliser Foo, Bar et Baz et consultez le programme ci-dessous.
Avant: la classe imbriquée n'est pas statique
class Fizz {
private Integer x = 12;
private static Integer y = 34;
class Buzz {
void m() {
x = 56;
y = 78;
System.out.println(x + y);
}
}
}
class FizzBuzzMain {
public static void main(String[] args) {
Fizz.Buzz fb = new Fizz().new Buzz();
fb.m();
}
}
Avant le résultat de l'exécution
134
C'est une classe imbriquée non statique, même si c'est (intentionnellement) fait. Maintenant, ajoutons simplement le modificateur static à cette classe imbriquée nommée Buzz
.
Après vente
class Fizz {
private Integer x = 12;
private static Integer y = 34;
static class Buzz {
void m() {
x = 56; //Impossible de compiler car x n'est pas un champ statique
y = 78;
System.out.println(x + y); //Impossible de compiler car x n'est pas un champ statique
}
}
}
class FizzBuzzMain {
public static void main(String[] args) {
Fizz.Buzz fb = new Fizz.Buzz();
fb.m();
}
}
Immédiatement, je n'ai pas pu compiler. L'EDI se met en colère s'il ne fonctionne pas car «x» n'est pas un champ statique.
Pourquoi. En effet, les ** classes imbriquées statiques ne peuvent accéder qu'aux membres statiques de la clôture **.
Pour l'application Android que vous vouliez créer, consultez ce GIF animé.
Dès que vous le démarrez, le décompte démarre automatiquement. Ce GIF animé est une animation qui se répète START! Quand il atteint 3, mais en réalité, cette application commence à partir de START! Et compte jusqu'à "9" et se termine (continue à afficher "9"). ..
Et le programme est le suivant. Même ceux qui n'ont aucune expérience dans le développement d'applications Android doivent être patients.
Classe d'écran
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
new MyTask().execute();
}
class MyTask extends AsyncTask<Void, String, Void> {
@Override
protected Void doInBackground(Void... voids) {
for (int i = 0; i < 10; i++) {
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]);
}
}
}
Si vous souhaitez créer une "classe d'écran" sur Android, commencez à coder une classe qui hérite de ʻAppCompatActivity`.
La classe TextView
est une vue (partie) qui affiche (peut uniquement) des chaînes de caractères placées au centre de l'écran et affiche" START! "," 0 "ou" 1 ".
Je veux changer ce TextView
de" 0 "à" 1 "puis à" 2 "par incréments de 1000 millisecondes par traitement asynchrone. C'est la condition requise pour cette application.
Si vous voulez un traitement asynchrone, définissez une sous-classe de ʻAsyncTask` et codez-y le traitement.
Si vous appelez la méthode ʻexecute () sur
MyTaskqui effectue le traitement asynchrone,
MyTaskcommencera le traitement. La boîte rouge dans cette image est «TextView».
MyTask écrase ce
TextView avec une chaîne telle que
"0" ,
"1" ou
"2" `. Cela donne l'impression que ça compte.
Ajoutons maintenant static
à la classe MyTask
.
L'IDE est devenu rouge en colère. Parce que le champ nommé textView
n'a pas le modificateur statique!
Veuillez jeter un œil à l'illustration de la femme qui monte les escaliers et du vieil homme à l'esprit terrestre.
Sur Android, les écrans qui ne sont plus nécessaires sont GC. L '«écran inutile» est lorsque l'utilisateur masque (éteint) l'écran en appuyant sur le bouton retour. En général, les appareils Android ont moins de mémoire que les PC [^ 2]. Par conséquent, l'écran masqué par l'utilisateur = l'instance de la classe Activity est rapidement GCed par le runtime.
[^ 2]: Les smartphones Android "gaming" avec 10 Go de RAM sont sur le marché, mais laissez-les tranquilles.
L'utilisateur appuie simplement sur le bouton retour, et cet écran = instance d'activité n'est pas nécessaire! Nja GC! Cela a tendance à être de courte durée et fatidique.
Donc, en arrière-plan (non, le terme "arrière-plan" n'est pas correct, il vaut mieux dire "dans le traitement asynchrone dans le thread de travail") MaTâche
est la mienne Vous vivrez jusqu'à ce que vous ayez terminé votre travail (compte à rebours).
C'est l'IDE appelé Android Studio qui m'a averti qu'il s'agissait d'une [instance englobante <instance de classe imbriquée] dans cette comparaison du temps de survie.
Démarrez cette application que j'ai créée, et même si le décompte commence en raison du travail de MyTask
, l'utilisateur appuie sur le bouton de retour, etc. pour masquer l'écran (éteindre, arrêter l'application) Même ainsi, ** l'objet MyTask
, qui est une instance de la classe imbriquée non statique, contient une référence implicite à l'instance MainActivity
**, donc il ne devrait plus être affiché, donc ce devrait être GC. De plus, il tombe dans un état où il n'est pas GC.
Là où l'utilisateur n'est pas visible, MyTask
effectue constamment un travail de décompte.
Dans l'illustration, la femme «MyTask» grimpe un pas à la fois. Mais dans sa main droite, il tient fermement le vieil homme «Activité principale» qui ne peut pas être un Bouddha. L'utilisateur ne peut pas voir ce vieil homme.
Je suis frappé par le dilemme ici.
--Ce MyTask
est une classe qui est utilisée uniquement dans cette MainActivity
, donc je veux en faire une classe imbriquée.
TextTview
est gênant (cette fois, TextTview
peut devenir une graine de fuite de mémoire si elle est gâchée par ce passage).
--C'est MainActivity
qui a (devrait avoir) TextTview
, et c'est MyTask
qui change la chaîne de caractères de cette TextTview
.
――Ainsi, après tout, je veux que cette MyTask
soit une classe imbriquée de cette MainActivity
.java.lang.ref.WeakReference <T>
Utilises tu ...Je m'inquiète à ce sujet tout le temps. Vous devrez peut-être faire un compromis quelque part.
c'est tout. Je m'inquiète pour ma conscience et j'aimerais écrire cet article avec les mots du professeur Satoshi Fukuzawa.
Cependant, la douleur d'être frappé par un mauvais enfant est plus douloureuse que l'épée infernale, et je n'ai pas d'autre choix que de frapper cette épée avec mon corps actuel.
_ Satoshi Fukuzawa "Education" 1878 (Meiji 11) _
Un petit Yukichi, "C'est plus douloureux que le blâme de l'enfer" est trop sévère pour être tiré. quitter. Je vais demander à M. Watsuji.
Avant la miséricorde de Michimoto, «le mal n'est pas forcément quelque chose à ridiculiser».
_ Tetsuro Watsuji "Etude de l'histoire spirituelle japonaise" 1922 (Taisho 11) _
c'est tout.