J'ai lu quelques articles sur les fuites de mémoire dans Android et j'ai regardé cette vidéo intéressante de Google I / O sur le sujet .
Pourtant, je ne comprends pas complètement le concept, et surtout quand il est sûr ou dangereux pour les classes internes des utilisateurs à l' intérieur d'une activité .
Voici ce que j'ai compris:
Une fuite de mémoire se produit si une instance d'une classe interne survit plus longtemps que sa classe externe (une activité). -> Dans quelles situations cela peut-il arriver?
Dans cet exemple, je suppose qu'il n'y a aucun risque de fuite, car il n'y a aucun moyen que l'extension de classe anonyme OnClickListener
vive plus longtemps que l'activité, non?
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_generic);
Button okButton = (Button) dialog.findViewById(R.id.dialog_button_ok);
TextView titleTv = (TextView) dialog.findViewById(R.id.dialog_generic_title);
// *** Handle button click
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
}
});
titleTv.setText("dialog title");
dialog.show();
Maintenant, cet exemple est-il dangereux et pourquoi?
// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);
private Runnable _droidPlayRunnable = new Runnable() {
public void run() {
_someFieldOfTheActivity.performLongCalculation();
}
};
J'ai un doute sur le fait que la compréhension de ce sujet a à voir avec la compréhension détaillée de ce qui est conservé lorsqu'une activité est détruite et recréée.
C'est ça?
Disons que je viens de changer l'orientation de l'appareil (qui est la cause la plus courante de fuites). Quand super.onCreate(savedInstanceState)
sera appelé dans mon onCreate()
, cela restaurera-t-il les valeurs des champs (telles qu'elles étaient avant le changement d'orientation)? Cela restaurera-t-il également les états des classes internes?
Je réalise que ma question n'est pas très précise, mais j'apprécierais vraiment toute explication qui pourrait clarifier les choses.
la source
Réponses:
Ce que vous demandez est une question assez difficile. Bien que vous puissiez penser qu'il ne s'agit que d'une seule question, vous posez en fait plusieurs questions à la fois. Je ferai de mon mieux en sachant que je dois le couvrir et, espérons-le, d'autres se joindront à moi pour couvrir ce qui pourrait me manquer.
Classes imbriquées: introduction
Comme je ne sais pas à quel point vous êtes à l'aise avec la POO en Java, cela touchera quelques bases. Une classe imbriquée est lorsqu'une définition de classe est contenue dans une autre classe. Il existe essentiellement deux types: les classes imbriquées statiques et les classes internes. La vraie différence entre ceux-ci sont:
Collecte des ordures et classes internes
Le garbage collection est automatique mais essaie de supprimer des objets selon qu'il pense qu'ils sont utilisés. Le garbage collector est assez intelligent, mais pas parfait. Il peut uniquement déterminer si quelque chose est utilisé, qu'il existe ou non une référence active à l'objet.
Le vrai problème ici est quand une classe interne a été maintenue en vie plus longtemps que son conteneur. Cela est dû à la référence implicite à la classe conteneur. Cela ne peut se produire que si un objet en dehors de la classe conteneur conserve une référence à l'objet interne, sans tenir compte de l'objet conteneur.
Cela peut conduire à une situation où l'objet interne est vivant (via une référence) mais les références à l'objet contenant ont déjà été supprimées de tous les autres objets. L'objet intérieur garde donc vivant l'objet contenant car il y aura toujours une référence. Le problème avec cela est qu'à moins qu'il ne soit programmé, il n'y a aucun moyen de revenir à l'objet contenant pour vérifier s'il est encore vivant.
L'aspect le plus important de cette prise de conscience est qu'il ne fait aucune différence que ce soit dans une activité ou dans un dessin. Vous devrez toujours être méthodique lorsque vous utilisez des classes internes et assurez-vous qu'elles ne survivent jamais aux objets du conteneur. Heureusement, si ce n'est pas un objet central de votre code, les fuites peuvent être faibles en comparaison. Malheureusement, ce sont quelques-unes des fuites les plus difficiles à trouver, car elles sont susceptibles de passer inaperçues jusqu'à ce que beaucoup d'entre elles aient fui.
Solutions: classes internes
Activités et opinions: Introduction
Les activités contiennent beaucoup d'informations pour pouvoir être exécutées et affichées. Les activités sont définies par la caractéristique qu'elles doivent avoir une vue. Ils ont également certains gestionnaires automatiques. Que vous le spécifiiez ou non, l'activité a une référence implicite à la vue qu'elle contient.
Pour qu'une vue soit créée, elle doit savoir où la créer et si elle a des enfants pour qu'elle puisse s'afficher. Cela signifie que chaque vue a une référence à l'activité (via
getContext()
). De plus, chaque vue conserve des références à ses enfants (c.getChildAt()
-à-d.). Enfin, chaque vue conserve une référence au bitmap rendu qui représente son affichage.Chaque fois que vous avez une référence à une activité (ou un contexte d'activité), cela signifie que vous pouvez suivre la chaîne ENTIÈRE dans la hiérarchie de la disposition. C'est pourquoi les fuites de mémoire concernant les activités ou les vues sont si importantes. Cela peut être une tonne de mémoire qui fuit en même temps.
Activités, vues et classes internes
Compte tenu des informations ci-dessus sur les classes internes, ce sont les fuites de mémoire les plus courantes, mais aussi les plus couramment évitées. Bien qu'il soit souhaitable d'avoir une classe interne ayant un accès direct aux membres d'une classe d'activités, beaucoup sont prêts à les rendre statiques pour éviter les problèmes potentiels. Le problème avec les activités et les vues va bien plus loin que cela.
Activités, vues et contextes d'activité qui ont fait l'objet d'une fuite
Tout se résume au contexte et au cycle de vie. Certains événements (tels que l'orientation) peuvent tuer un contexte d'activité. Étant donné que tant de classes et de méthodes nécessitent un contexte, les développeurs essaient parfois de sauvegarder du code en saisissant une référence à un contexte et en le conservant. Il se trouve que bon nombre des objets que nous devons créer pour exécuter notre activité doivent exister en dehors du cycle de vie de l'activité afin de permettre à l'activité de faire ce qu'elle doit faire. Si l'un de vos objets se trouve avoir une référence à une activité, son contexte ou l'une de ses vues lorsqu'il est détruit, vous venez de divulguer cette activité et toute son arborescence de vues.
Solutions: activités et vues
getBaseContext()
ougetApplicationContext()
). Ceux-ci ne conservent pas implicitement les références.Runnables: Introduction
Les runnables ne sont en fait pas si mal. Je veux dire, ils pourraient l' être, mais vraiment nous avons déjà touché la plupart des zones de danger. Une Runnable est une opération asynchrone qui exécute une tâche indépendante du thread sur lequel elle a été créée. La plupart des exécutables sont instanciés à partir du thread d'interface utilisateur. En substance, l'utilisation d'un Runnable crée un autre thread, juste un peu plus géré. Si vous classe un Runnable comme une classe standard et suivez les instructions ci-dessus, vous devriez rencontrer quelques problèmes. La réalité est que de nombreux développeurs ne le font pas.
Par facilité, lisibilité et flux de programme logique, de nombreux développeurs utilisent des classes internes anonymes pour définir leurs Runnables, comme l'exemple que vous créez ci-dessus. Cela donne un exemple comme celui que vous avez tapé ci-dessus. Une classe intérieure anonyme est fondamentalement une classe intérieure discrète. Vous n'avez tout simplement pas à créer une toute nouvelle définition et simplement remplacer les méthodes appropriées. À tous les autres égards, il s'agit d'une classe interne, ce qui signifie qu'elle conserve une référence implicite à son conteneur.
Runnables et activités / vues
Yay! Cette section peut être courte! En raison du fait que les Runnables s'exécutent en dehors du thread actuel, le danger avec celles-ci vient des opérations asynchrones de longue durée. Si le fichier exécutable est défini dans une activité ou une vue comme une classe interne anonyme OU une classe interne imbriquée, il existe des dangers très graves. En effet, comme indiqué précédemment, il doit savoir qui est son conteneur. Entrez le changement d'orientation (ou la suppression du système). Reportez-vous maintenant aux sections précédentes pour comprendre ce qui vient de se passer. Oui, votre exemple est assez dangereux.
Solutions: Runnables
Répondre à la dernière question maintenant pour répondre aux questions qui n'ont pas été directement abordées par les autres sections de cet article. Vous avez demandé "Quand un objet d'une classe interne peut-il survivre plus longtemps que sa classe externe?" Avant d'en arriver là, permettez-moi de le souligner à nouveau: bien que vous ayez raison de vous en préoccuper dans Activités, cela peut provoquer une fuite n'importe où. Je vais fournir un exemple simple (sans utiliser d'activité) juste pour démontrer.
Voici un exemple courant d'une usine de base (sans le code).
Ce n'est pas un exemple aussi courant, mais assez simple à démontrer. La clé ici est le constructeur ...
Maintenant, nous avons des fuites, mais pas d'usine. Même si nous avons sorti Factory, il restera en mémoire car chaque fuite a une référence. Peu importe que la classe externe ne dispose pas de données. Cela se produit beaucoup plus souvent qu'on ne pourrait le penser. Nous n'avons pas besoin du créateur, juste de ses créations. Nous en créons donc un temporairement, mais utilisons les créations indéfiniment.
Imaginez ce qui se passe lorsque nous changeons légèrement le constructeur.
Maintenant, chacun de ces nouveaux LeakFactories vient d'être divulgué. Que penses-tu de cela? Ce sont deux exemples très courants de la façon dont une classe interne peut survivre à une classe externe de n'importe quel type. Si cette classe externe avait été une activité, imaginez à quel point cela aurait été pire.
Conclusion
Ceux-ci répertorient les principaux dangers connus de l'utilisation inappropriée de ces objets. En général, ce message aurait dû couvrir la plupart de vos questions, mais je comprends qu'il s'agissait d'un long message, donc si vous avez besoin de clarifications, faites le moi savoir. Tant que vous suivrez les pratiques ci-dessus, vous aurez très peu de soucis de fuite.
la source