如何创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
会将自己与错误的线程关联起来。
这似乎不是一个罕见的情况。 我可以想出几个解决方法,他们都是丑陋的:
-
像这样的东西:
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()
之前,那么我们永远不会被唤醒! -
将一个初始
Message
传递给Worker
的构造函数,使run()
方法发布它。 临时解决scheme不适用于多个邮件,或者如果我们不想马上发送邮件, -
忙于等待
handler
字段不再为null
。 是的,最后的手段…
我想代表Worker
线程创build一个Handler
和MessageQueue
,但这似乎不可能。 什么是最优雅的方式呢?
最终解决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(); } } }