Cómo crear un hilo Looper, a continuación, enviar un mensaje de inmediato?

Tengo un hilo del trabajador que se sienta en el fondo, procesando mensajes. Algo como esto:

class Worker extends Thread { public volatile Handler handler; // actually private, of course public void run() { Looper.prepare(); mHandler = new Handler() { // the Handler hooks up to the current Thread public boolean handleMessage(Message msg) { // ... } }; Looper.loop(); } } 

Desde el hilo principal (hilo de interfaz de usuario, no importa) Me gustaría hacer algo como esto:

 Worker worker = new Worker(); worker.start(); worker.handler.sendMessage(...); 

El problema es que esto me prepara para una condición de carrera hermosa: en el momento en que se lee worker.handler , no hay manera de estar seguro de que el hilo de trabajo ya ha asignado a este campo!

No puedo simplemente crear el Handler desde el constructor del Worker , porque el constructor se ejecuta en el subproceso principal, por lo que el Handler se asociará con el subproceso incorrecto.

Esto apenas parece un escenario poco común. Puedo llegar a varias soluciones, todas feas:

  1. Algo como esto:

     class Worker extends Thread { public volatile Handler handler; // actually private, of course public void run() { Looper.prepare(); mHandler = new Handler() { // the Handler hooks up to the current Thread public boolean handleMessage(Message msg) { // ... } }; notifyAll(); // <- ADDED Looper.loop(); } } 

    Y desde el hilo principal:

     Worker worker = new Worker(); worker.start(); worker.wait(); // <- ADDED worker.handler.sendMessage(...); 

    Pero esto no es confiable tampoco: si el notifyAll() sucede antes de la wait() , entonces nunca seremos despertados!

  2. Pasar un Message inicial al constructor del Worker , haciendo que el método run() publique. Una solución ad-hoc, no funciona para varios mensajes, o si no queremos enviarlo de inmediato, pero poco después.

  3. Ocupado en espera hasta que el campo del handler ya no sea null . Sí, un último recurso …

Me gustaría crear un Handler y MessageQueue en nombre del hilo Worker , pero esto no parece ser posible. ¿Cuál es la forma más elegante de salir de esto?

Solución eventual (menos comprobación de errores), gracias a CommonsWare:

 class Worker extends HandlerThread { // ... public synchronized void waitUntilReady() { d_handler = new Handler(getLooper(), d_messageHandler); } } 

Y desde el hilo principal:

 Worker worker = new Worker(); worker.start(); worker.waitUntilReady(); // <- ADDED worker.handler.sendMessage(...); 

Esto funciona gracias a la semántica de HandlerThread.getLooper() que bloquea hasta que el looper ha sido inicializado.


Por cierto, esto es similar a mi solución # 1 anterior, ya que el HandlerThread se implementa aproximadamente como sigue (gotta love open source):

 public void run() { Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Looper.loop(); } public Looper getLooper() { synchronized (this) { while (mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } 

La diferencia clave es que no comprueba si el subproceso de trabajo está en ejecución, sino que realmente ha creado un looper; Y la manera de hacerlo es almacenar el looper en un campo privado. ¡Bonito!

HandlerThread un vistazo al código fuente de HandlerThread

 @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } 

Básicamente, si está extendiendo Thread en worker e implementando su propio Looper, entonces su clase de thread principal debería extender a worker y establecer su handler allí.

  class WorkerThread extends Thread { private Exchanger<Void> mStartExchanger = new Exchanger<Void>(); private Handler mHandler; public Handler getHandler() { return mHandler; } @Override public void run() { Looper.prepare(); mHandler = new Handler(); try { mStartExchanger.exchange(null); } catch (InterruptedException e) { e.printStackTrace(); } Looper.loop(); } @Override public synchronized void start() { super.start(); try { mStartExchanger.exchange(null); } catch (InterruptedException e) { e.printStackTrace(); } } } 

Esta es mi solución: MainActivity:

 //Other Code mCountDownLatch = new CountDownLatch(1); mainApp = this; WorkerThread workerThread = new WorkerThread(mCountDownLatch); workerThread.start(); try { mCountDownLatch.await(); Log.i("MsgToWorkerThread", "Worker Thread is up and running. We can send message to it now..."); } catch (InterruptedException e) { e.printStackTrace(); } Toast.makeText(this, "Trial run...", Toast.LENGTH_LONG).show(); Message msg = workerThread.workerThreadHandler.obtainMessage(); workerThread.workerThreadHandler.sendMessage(msg); 

La clase WorkerThread:

 public class WorkerThread extends Thread{ public Handler workerThreadHandler; CountDownLatch mLatch; public WorkerThread(CountDownLatch latch){ mLatch = latch; } public void run() { Looper.prepare(); workerThreadHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.i("MsgToWorkerThread", "Message received from UI thread..."); MainActivity.getMainApp().runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.getMainApp().getApplicationContext(), "Message received in worker thread from UI thread", Toast.LENGTH_LONG).show(); //Log.i("MsgToWorkerThread", "Message received from UI thread..."); } }); } }; Log.i("MsgToWorkerThread", "Worker thread ready..."); mLatch.countDown(); Looper.loop(); } } 
  • Pasar variables entre el renderizador y otra clase con queueEvent ()
  • ¿Cómo puedo ejecutar el código en el hilo de la interfaz de usuario en una prueba de Android JUnit sin una actividad?
  • Android - Prevenga la pantalla blanca al inicio
  • Hacer algunas cosas de la interfaz de usuario de Android en un subproceso no UI
  • Método de dibujo de Android que se ejecuta lento
  • Iniciar nuevo subproceso en Async Task
  • Qué cosas se ejecutan en el hilo principal de la interfaz de usuario al abrir una aplicación para Android
  • PARTIAL_WAKE_LOCK vs SCREEN_DIM_WAKE_LOCK en el subproceso de descarga
  • Handler.post (runnable) no ejecuta siempre el método run en android
  • TarsosDSP y SurfaceView múltiple problema de subprocesamiento
  • Realizar cambios en la interfaz de usuario de la actividad principal desde el subproceso en Service
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.