He estado leyendo algunos artículos sobre pérdidas de memoria en Android y vi este interesante video de Google I / O sobre el tema .
Aún así, no entiendo completamente el concepto, y especialmente cuando es seguro o peligroso para las clases internas del usuario dentro de una Actividad .
Esto es lo que entendí:
Se producirá una pérdida de memoria si una instancia de una clase interna sobrevive más tiempo que su clase externa (una Actividad). -> ¿ En qué situaciones puede suceder esto?
En este ejemplo, supongo que no hay riesgo de fuga, porque no hay forma de que la extensión anónima de la clase OnClickListener
viva más tiempo que la actividad, ¿verdad?
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();
Ahora, ¿es peligroso este ejemplo y por qué?
// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);
private Runnable _droidPlayRunnable = new Runnable() {
public void run() {
_someFieldOfTheActivity.performLongCalculation();
}
};
Tengo una duda sobre el hecho de que comprender este tema tiene que ver con comprender en detalle lo que se guarda cuando una actividad se destruye y se vuelve a crear.
¿Lo es?
Digamos que acabo de cambiar la orientación del dispositivo (que es la causa más común de fugas). ¿Cuándo super.onCreate(savedInstanceState)
se llamará en mi onCreate()
, esto restaurará los valores de los campos (como estaban antes del cambio de orientación)? ¿Esto también restaurará los estados de las clases internas?
Me doy cuenta de que mi pregunta no es muy precisa, pero agradecería cualquier explicación que pudiera aclarar las cosas.
fuente
Respuestas:
Lo que preguntas es una pregunta bastante difícil. Si bien puede pensar que es solo una pregunta, en realidad está haciendo varias preguntas a la vez. Haré lo mejor que pueda con el conocimiento de que tengo que cubrirlo y, con suerte, algunos otros se unirán para cubrir lo que pueda extrañar.
Clases Anidadas: Introducción
Como no estoy seguro de qué tan cómodo se siente con OOP en Java, esto afectará algunos aspectos básicos. Una clase anidada es cuando una definición de clase está contenida dentro de otra clase. Básicamente hay dos tipos: Clases anidadas estáticas y Clases internas. La verdadera diferencia entre estos son:
Recolección de basura y clases internas
La recolección de basura es automática, pero intenta eliminar objetos según si cree que se están utilizando. El recolector de basura es bastante inteligente, pero no perfecto. Solo puede determinar si se está usando algo si existe o no una referencia activa al objeto.
El problema real aquí es cuando una clase interna se ha mantenido viva más tiempo que su contenedor. Esto se debe a la referencia implícita a la clase que lo contiene. La única forma en que esto puede ocurrir es si un objeto fuera de la clase que contiene mantiene una referencia al objeto interno, sin tener en cuenta el objeto que lo contiene.
Esto puede conducir a una situación en la que el objeto interno está vivo (por referencia) pero las referencias al objeto que lo contiene ya se han eliminado de todos los demás objetos. El objeto interno, por lo tanto, mantiene vivo al objeto contenedor porque siempre tendrá una referencia a él. El problema con esto es que, a menos que esté programado, no hay forma de volver al objeto que lo contiene para verificar si está vivo.
El aspecto más importante para esta realización es que no importa si se trata de una Actividad o de un sorteo. Usted siempre tiene que ser metódico cuando se utilizan las clases internas y asegurarse de que los objetos no sobreviven del contenedor. Afortunadamente, si no es un objeto central de su código, las filtraciones pueden ser pequeñas en comparación. Desafortunadamente, estas son algunas de las filtraciones más difíciles de encontrar, ya que es probable que pasen desapercibidas hasta que muchas de ellas se hayan filtrado.
Soluciones: clases internas
Actividades y vistas: Introducción
Las actividades contienen mucha información para poder ejecutarse y visualizarse. Las actividades se definen por la característica de que deben tener una Vista. También tienen ciertos manejadores automáticos. Ya sea que lo especifique o no, la Actividad tiene una referencia implícita a la Vista que contiene.
Para que se cree una Vista, debe saber dónde crearla y si tiene hijos para poder mostrarla. Esto significa que cada Vista tiene una referencia a la Actividad (vía
getContext()
). Además, cada vista mantiene referencias a sus elementos secundarios (es decirgetChildAt()
). Finalmente, cada vista mantiene una referencia al mapa de bits representado que representa su visualización.Siempre que tenga una referencia a una Actividad (o Contexto de Actividad), esto significa que puede seguir TODA la cadena en la jerarquía de diseño. Es por eso que las pérdidas de memoria con respecto a las actividades o vistas son tan importantes. Puede ser una tonelada de memoria que se filtró de una vez.
Actividades, vistas y clases internas
Dada la información anterior sobre las clases internas, estas son las pérdidas de memoria más comunes, pero también las que se evitan con mayor frecuencia. Si bien es deseable que una clase interna tenga acceso directo a los miembros de una clase de Actividades, muchos están dispuestos a hacerlos estáticos para evitar posibles problemas. El problema con Actividades y Vistas es mucho más profundo que eso.
Actividades filtradas, vistas y contextos de actividad
Todo se reduce al Contexto y al Ciclo de Vida. Hay ciertos eventos (como la orientación) que matarán un contexto de actividad. Dado que muchas clases y métodos requieren un Contexto, los desarrolladores a veces intentarán guardar algo de código tomando una referencia a un Contexto y reteniéndolo. Sucede que muchos de los objetos que tenemos que crear para ejecutar nuestra Actividad tienen que existir fuera del Activity LifeCycle para permitir que la Actividad haga lo que necesita hacer. Si alguno de sus objetos tiene una referencia a una Actividad, su Contexto o cualquiera de sus Vistas cuando se destruye, acaba de filtrar esa Actividad y todo su árbol de Vistas.
Soluciones: actividades y puntos de vista
getBaseContext()
ogetApplicationContext()
). Estos no guardan referencias implícitamente.Runnables: Introducción
Los runnables en realidad no son tan malos. Quiero decir, podrían ser, pero realmente ya hemos golpeado la mayoría de las zonas de peligro. Un Runnable es una operación asincrónica que realiza una tarea independiente del hilo en el que se creó. La mayoría de las ejecutables se instancian desde el hilo de la interfaz de usuario. En esencia, usar un Runnable es crear otro hilo, solo un poco más administrado. Si clasifica un Runnable como una clase estándar y sigue las pautas anteriores, debería tener pocos problemas. La realidad es que muchos desarrolladores no hacen esto.
Debido a la facilidad, la legibilidad y el flujo lógico del programa, muchos desarrolladores utilizan clases internas anónimas para definir sus Runnables, como el ejemplo que creó anteriormente. Esto da como resultado un ejemplo como el que escribió anteriormente. Una clase interna anónima es básicamente una clase interna discreta. Simplemente no tiene que crear una definición completamente nueva y simplemente anular los métodos apropiados. En todos los demás aspectos, es una clase interna, lo que significa que mantiene una referencia implícita a su contenedor.
Runnables y Actividades / Vistas
¡Hurra! ¡Esta sección puede ser corta! Debido al hecho de que los Runnables se ejecutan fuera del hilo actual, el peligro con estos viene a ser operaciones asincrónicas de larga duración. Si el ejecutable se define en una Actividad o Vista como una Clase interna anónima O una Clase interna anidada, existen algunos peligros muy graves. Esto se debe a que, como se indicó anteriormente, tiene que saber quién es su contenedor. Ingrese el cambio de orientación (o la eliminación del sistema). Ahora solo consulte las secciones anteriores para comprender lo que acaba de suceder. Sí, tu ejemplo es bastante peligroso.
Soluciones: Runnables
Respondiendo la pregunta final ahora para responder las preguntas que no fueron abordadas directamente por las otras secciones de esta publicación. Usted preguntó "¿Cuándo puede un objeto de una clase interna sobrevivir más tiempo que su clase externa?" Antes de llegar a esto, permítanme enfatizar de nuevo: aunque tiene razón en preocuparse por esto en Actividades, puede causar una fuga en cualquier lugar. Proporcionaré un ejemplo simple (sin usar una Actividad) solo para demostrarlo.
A continuación se muestra un ejemplo común de una fábrica básica (falta el código).
Este no es un ejemplo común, pero es lo suficientemente simple como para demostrarlo. La clave aquí es el constructor ...
Ahora, tenemos fugas, pero no fábrica. Aunque lanzamos Factory, permanecerá en la memoria porque cada Leak tiene una referencia a ella. Ni siquiera importa que la clase externa no tenga datos. Esto sucede mucho más a menudo de lo que uno podría pensar. No necesitamos al creador, solo sus creaciones. Entonces creamos uno temporalmente, pero usamos las creaciones indefinidamente.
Imagina lo que sucede cuando cambiamos el constructor solo ligeramente.
Ahora, cada una de esas nuevas LeakFactories acaba de filtrarse. ¿Qué piensas de eso? Esos son dos ejemplos muy comunes de cómo una clase interna puede sobrevivir a una clase externa de cualquier tipo. Si esa clase externa hubiera sido una Actividad, imagina lo peor que hubiera sido.
Conclusión
Estos enumeran los peligros principalmente conocidos de usar estos objetos de manera inapropiada. En general, esta publicación debería haber cubierto la mayoría de sus preguntas, pero entiendo que fue una publicación muuuy larga, así que si necesita aclaraciones, hágamelo saber. Siempre que siga las prácticas anteriores, tendrá muy poca preocupación por las fugas.
fuente