我有一个工作线程,它位于后台,处理消息。像这样:
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
时,无法确定工作线程是否已分配给该字段!
我不能简单地从Handler
的构造函数创建Worker
,因为构造函数在主线程上运行,因此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()
方法发布它。一个临时解决方案不适用于多条消息,或者我们不想立即发送,但很快就会发送。
忙碌等待handler
字段不再是null
。是的,不得已......
我想代表Handler
线程创建MessageQueue
和Worker
,但这似乎不可能。什么是最优雅的方式?
答案 0 :(得分:60)
最终解决方案(减去错误检查),感谢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初始化之前一直阻塞。
顺便说一下,这与我上面的解决方案#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;
}
关键的区别在于它不会检查工作线程是否正在运行,而是它实际上已经创建了一个looper;并且这样做的方法是将活套存放在私人区域。尼斯!
答案 1 :(得分:1)
这是我的解决方案: 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();
}
}
答案 2 :(得分:0)
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();
}
}
}