Моя программа бросила NullPointerException на днях, когда она пыталась использовать Обработчик, созданный на другом потоке, чтобы отправить тому потоку сообщение. Обработчик, созданный другим потоком, еще не был создан или еще не видимый к вызывающему потоку, несмотря на вызывающий поток, уже звонивший, запускаются на другом потоке. Это только происходит очень редко. Почти каждый тестовый прогон не получает исключение.
Я задавался вопросом, что лучший способ состоит в том, чтобы избежать этой проблемы наверняка с минимальной сложностью и потерей производительности. Программа является игрой и очень чувствительной производительностью, особенно после того как она работает. Поэтому я стараюсь не использовать синхронизацию после установки, например, и предпочел бы не вращаться на переменной в любое время.
Фон:
В Android класс Обработчика может использоваться для "постановки в очередь действия, которое будет выполнено на другом потоке, чем собственное". Документация здесь:
http://developer.android.com/intl/de/reference/android/os/Handler.html
Обработчик должен быть создан на потоке, где он будет использоваться. Так создание его в конструкторе потока, который выполняется потоком, создающим тот поток, не является опцией.
Когда Обработчик для потока кроме потока UI, класс Выполняющего мертвую петлю летчика должен также использоваться:
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
С кодом в потоке создания, бывшем похожем на это:
LooperThread otherThread = new LooperThread();
otherThread.start();
otherThread.waitForSetupComplete();
otherThread.mHandler.sendEmptyMessage(0);
Есть ли какие-либо лучшие решения?Спасибо.
Подготовка 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) {
// ...
}
}
Я бы пошел с Classic 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.