Mi programa arrojó una NullPointerException el otro día cuando intentó usar un controlador creado en otro hilo para enviar un mensaje a ese hilo. El controlador creado por el otro hilo aún no se ha creado, o aún no es visible para el hilo de llamada, a pesar de que el hilo de llamada ya ha llamado a iniciar el otro hilo. Esto solo ocurre muy raramente. Casi cada ejecución de prueba no obtiene la excepción.¿Cómo me aseguro de que otro Thread's Handler no sea nulo antes de llamarlo?
Me preguntaba cuál es la mejor manera de evitar este problema con una complicación mínima y una penalización de rendimiento. El programa es un juego muy sensible al rendimiento, especialmente una vez que se está ejecutando. Por lo tanto, trato de evitar usar la sincronización después de la configuración, por ejemplo, y preferiría evitar girar en una variable en cualquier momento.
Antecedentes:
En Android, la clase Handler se puede utilizar para "poner en cola una acción que se realizará en un hilo diferente al suyo". Documentación aquí:
http://developer.android.com/intl/de/reference/android/os/Handler.html
El controlador debe crearse en la secuencia donde se utilizará. Entonces, crearlo en el constructor de un hilo, que es ejecutado por el hilo que crea ese hilo, no es una opción.
cuando el manejador es para un subproceso distinto del hilo de interfaz de usuario, la clase Looper debe usarse también:
http://developer.android.com/intl/de/reference/android/os/Looper.html
La documentación da a este ejemplo de uso de las dos clases para este propósito:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Mi muy feo solución actualmente se ve así:
public class LooperThread extends Thread {
public volatile Handler mHandler;
public final ArrayBlockingQueue<Object> setupComplete = new ArrayBlockingQueue<Object>(1);
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
setupComplete();
Looper.loop();
}
public void waitForSetupComplete() {
while (true) {
try {
setupComplete.take();
return;
} catch (InterruptedException e) {
//Ignore and try again.
}
}
}
private void setupComplete() {
while(true) {
try {
setupComplete.put(new Object());
return;
} catch (InterruptedException e) {
//Ignore and try again.
}
}
}
}
Con el bacalao e en el hilo de creación que se ve así:
LooperThread otherThread = new LooperThread();
otherThread.start();
otherThread.waitForSetupComplete();
otherThread.mHandler.sendEmptyMessage(0);
¿Hay alguna solución mejor? Gracias.
Ah, bien. HandlerThread # getLooper puede hacer el bloqueo en lugar de tener que lidiar con la complejidad adicional. Para mi aplicación particular, ni siquiera necesito la subclase HandlerThread. –