如何创buildLooper线程,然后立即发送消息?

我有一个工作线程,在后台,处理消息。 像这样的东西:

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(); } } 

从主线程(UI线程,并不重要)我想要做这样的事情:

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

麻烦的是,这使我有一个美丽的竞争条件:在读取worker.handler ,没有办法确定工作线程已经分配给这个字段!

我不能简单地从Worker的构造函数中创buildHandler ,因为构造函数在主线程上运行,所以Handler会将自己与错误的线程关联起来。

这似乎不是一个罕见的情况。 我可以想出几个解决方法,他们都是丑陋的:

  1. 像这样的东西:

     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(); } } 

    并从主线程:

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

    但是这也不可靠:如果notifyAll()发生在wait()之前,那么我们永远不会被唤醒!

  2. 将一个初始Message传递给Worker的构造函数,使run()方法发布它。 临时解决scheme不适用于多个邮件,或者如果我们不想马上发送邮件,

  3. 忙于等待handler字段不再为null 。 是的,最后的手段…

我想代表Worker线程创build一个HandlerMessageQueue ,但这似乎不可能。 什么是最优雅的方式呢?

最终解决scheme(减去错误检查),感谢CommonsWare:

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

并从主线程:

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

这工作得益于HandlerThread.getLooper()的语义,它会阻塞到Looper初始化。


顺便说一句,这与我上面的解决scheme#1类似,因为HandlerThread大致如下实现(得爱开源):

 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; } 

关键的区别在于它不检查工作线程是否正在运行,但它确实创build了一个活套; 而这样做的方式是将活套存放在私人领域。 太好了!

看看HandlerThread的源代码

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

基本上,如果你在Worker中扩展Thread并实现自己的Looper,那么你的主线程类应该扩展worker并在那里设置你的handler。

这是我的解决scheme: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); 

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(); } } 
  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(); } } }