En la programación común de Android, Handler se usa a menudo al realizar operaciones asincrónicas y procesar resultados devueltos. Normalmente nuestro código se implementa así.
Copie el código de código de la siguiente manera:
clase pública SampleActivity extiende Actividad {
Controlador final privado mLeakyHandler = nuevo controlador () {
@Anular
mensaje de mango público vacío (mensaje de mensaje) {
//...
}
}
}
Sin embargo, de hecho, el código anterior puede provocar pérdidas de memoria. Cuando utilice la herramienta lint de Android, recibirá dicha advertencia.
Copie el código de código de la siguiente manera:
En Android, las clases de Handler deben ser estáticas o pueden ocurrir fugas. Los mensajes en cola en MessageQueue del hilo de la aplicación también conservan su Handler de destino. Si el Handler es una clase interna, su clase externa también se conservará para evitar fugas de la clase externa. declarar el controlador como una clase anidada estática con una referencia débil a su clase externa
Al ver esto, es posible que todavía esté confundido. ¿En qué parte del código puede causar una pérdida de memoria y cómo puede causar una pérdida de memoria? Entonces analicémoslo lentamente.
1. Cuando se inicia una aplicación de Android, se crea automáticamente una instancia de Looper para que la utilice el hilo principal de la aplicación. El trabajo principal de Looper es procesar los objetos de mensaje en la cola de mensajes uno por uno. En Android, todos los eventos del marco de Android (como las llamadas al método del ciclo de vida de la actividad y los clics en botones, etc.) se colocan en mensajes y luego se agregan a la cola de mensajes para que Looper los procese, y Looper es responsable de procesarlos uno por uno. por uno. El ciclo de vida del Looper en el hilo principal es tan largo como la aplicación actual.
2. Cuando se inicializa un controlador en el hilo principal y enviamos un mensaje dirigido a este controlador a la cola de mensajes procesada por Looper, el mensaje realmente enviado ya contiene una referencia a una instancia de controlador. Solo de esta manera Looper procesa solo cuando esto. Cuando se recibe el mensaje, se puede llamar a Handler#handleMessage(Message) para completar el procesamiento correcto del mensaje.
3. En Java, las clases internas no estáticas y las clases internas anónimas contienen implícitamente referencias a sus clases externas. Las clases internas estáticas no contienen referencias a clases externas. Para obtener más información sobre esto, consulte Chat Java: modificador privado "no válido"
Es cierto que el ejemplo de código anterior es un poco difícil de detectar la pérdida de memoria, entonces el siguiente ejemplo es muy obvio
Copie el código de código de la siguiente manera:
clase pública SampleActivity extiende Actividad {
Controlador final privado mLeakyHandler = nuevo controlador () {
@Anular
mensaje de mango público vacío (mensaje de mensaje) {
//...
}
}
@Anular
vacío protegido onCreate (paquete estado de instancia guardado) {
super.onCreate(savedInstanceState);
// Publicar un mensaje y retrasar su ejecución durante 10 minutos.
mLeakyHandler.postDelayed(nuevo Runnable() {
@Anular
ejecución pública vacía() { /* ... */ }
}, 1000 * 60 * 10);
// Regresar a la Actividad anterior.
finalizar();
}
}
Analizando el código anterior, cuando ejecutamos el método de finalización de la actividad, el mensaje retrasado existirá en la cola de mensajes del hilo principal durante 10 minutos antes de ser procesado, y este mensaje contiene una referencia al Controlador, y el Controlador es una instancia anónima de la clase interna contiene una referencia a SampleActivity externa, por lo que esto hace que SampleActivity no se pueda reciclar. Como resultado, muchos recursos retenidos por SampleActivity no se pueden reciclar. Esto es lo que a menudo llamamos una pérdida de memoria.
Tenga en cuenta que el nuevo Runnable anterior también se implementa mediante una clase interna anónima. También contiene una referencia a SampleActivity y evita que SampleActivity se recicle.
Para resolver este problema, la idea es no utilizar clases internas no estáticas. Al heredar Handler, colóquelo en un archivo de clase separado o use una clase interna estática. Debido a que la clase interna estática no contiene una referencia a la clase externa, no causará pérdidas de memoria en la instancia de la clase externa. Cuando necesitamos llamar a una Actividad externa en una clase interna estática, podemos usar referencias débiles para manejarla. Además, Runnable también debe configurarse como un atributo de miembro estático. Nota: Una instancia de clase interna anónima estática no contiene una referencia a la clase externa. El código que no provocará pérdidas de memoria después de la modificación es el siguiente:
Copie el código de código de la siguiente manera:
clase pública SampleActivity extiende Actividad {
/**
* Las instancias de clases internas estáticas no tienen un valor implícito.
* referencia a su clase exterior.
*/
clase estática privada MyHandler extiende Handler {
Private final WeakReference<SampleActivity> mActivity;
público MyHandler (actividad de actividad de muestra) {
mActivity = new WeakReference<SampleActivity>(actividad);
}
@Anular
mensaje de mango público vacío (mensaje de mensaje) {
Actividad SampleActivity = mActivity.get();
si (actividad! = nulo) {
//...
}
}
}
privado final MyHandler mHandler = nuevo MyHandler (este);
/**
* Las instancias de clases anónimas no tienen un valor implícito.
* referencia a su clase externa cuando son "estáticos".
*/
Runnable final estático privado sRunnable = nuevo Runnable() {
@Anular
ejecución pública vacía() { /* ... */ }
};
@Anular
vacío protegido onCreate (paquete estado de instancia guardado) {
super.onCreate(savedInstanceState);
// Publicar un mensaje y retrasar su ejecución durante 10 minutos.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Regresar a la Actividad anterior.
finalizar();
}
}
De hecho, muchas pérdidas de memoria en Android se deben al uso de clases internas no estáticas en la Actividad, como se menciona en este artículo, por lo que debemos prestar especial atención al usar clases internas no estáticas en el ciclo de vida de su instancia. el objeto retenido es mayor que su objeto de clase externo, puede provocar pérdidas de memoria. Personalmente, tiendo a utilizar la clase estática del artículo y los métodos de referencia débiles para resolver este problema.