我的程序在前几天尝试使用在另一个线程上创建的Handler向该线程发送消息时抛出了NullPointerException。尽管调用线程已经在另一个线程上调用了start,但是其他线程创建的Handler尚未创建,或者尚未对调用线程可见。这种情况很少发生。几乎每次测试都没有得到例外。
我想知道什么是最好的方法来避免这个问题,确保最小的复杂性和性能损失。该程序是一款游戏,性能非常敏感,特别是一旦运行。因此,我尝试避免在设置后使用同步,例如,并且宁愿在任何时候避免旋转变量。
背景:
在Android中,Handler类可用于“将行为排入队列,而不是在您自己的线程上执行”。文档:
http://developer.android.com/intl/de/reference/android/os/Handler.html
必须在将使用它的线程上创建Handler。因此,在创建该线程的线程运行的线程的构造函数中创建它不是一个选项。
当Handler用于UI线程以外的线程时,还必须使用Looper类:
http://developer.android.com/intl/de/reference/android/os/Looper.html
文档给出了为此目的使用这两个类的示例:
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();
}
}
我非常丑陋的解决方法目前看起来像这样:
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.
}
}
}
}
创建线程中的代码如下所示:
LooperThread otherThread = new LooperThread();
otherThread.start();
otherThread.waitForSetupComplete();
otherThread.mHandler.sendEmptyMessage(0);
有没有更好的解决方案?感谢。
答案 0 :(得分:12)
我会选择经典的wait / notify
public class LooperThread extends Thread {
private Handler mHandler;
public void run() {
Looper.prepare();
synchronized (this) {
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
notifyAll();
}
Looper.loop();
}
public synchronized Handler getHandler() {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException e) {
//Ignore and try again.
}
}
return mHandler;
}
}
然后可以多次使用从getHandler返回的处理程序,而无需调用同步的getHandler。
答案 1 :(得分:5)
准备Looper
可以阻止一段时间,所以我想你会遇到prepare()
需要一点时间才能完成的情况,因此mHandler
仍未定义。
您可以Thread
延长HandlerThread
,但即使这样,您仍需等待确保Looper
已初始化。也许这样的事情可能有用,你可以单独定义Handler
,但是使用自定义线程的Looper
。
可能。
private void setUp() {
mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
// Create our handler; this will block until looper is initialised
mHandler = new CustomHandler(mHandlerThread.getLooper());
// mHandler is now ready to use
}
private class CustomThread extends HandlerThread {
public void run() {
// ...
}
}
private class CustomHandler extends Handler {
CustomHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// ...
}
}
答案 2 :(得分:1)
我只想补充一点,检查答案是最好的答案,但如果你测试它就不会起作用因为你需要在运行方法上调用super,因为它负责准备looper所以代码应该是像这样:
private void setUp() {
mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
// Create our handler; this will block until looper is initialised
mHandler = new CustomHandler(mHandlerThread.getLooper());
// mHandler is now ready to use
}
private class CustomThread extends HandlerThread {
public void run() {
super.run() // <- VERY IMPORTANT OTHERWISE IT DOES NOT WORK
// your code goes here
}
}
private class CustomHandler extends Handler {
CustomHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// ...
}
}