Моя программа вызвала исключение NullPointerException на днях, когда она пыталась использовать обработчик, созданный в другом потоке, для отправки этому потоку сообщения. Обработчик, созданный другим потоком, еще не был создан или еще не виден вызывающему потоку, несмотря на то, что вызывающий поток уже вызвал start в другом потоке. Это случается очень редко. Почти каждый тестовый прогон не получает исключения.
Мне было интересно, как лучше всего избежать этой проблемы с минимальными сложностями и ухудшением производительности. Программа представляет собой игру и очень чувствительна к производительности, особенно после запуска. Поэтому я стараюсь, например, избегать использования синхронизации после настройки и предпочитаю избегать вращения переменной в любое время.
Справочная информация:
В Android класс Handler может использоваться для «постановки в очередь действия, которое должно быть выполнено в другом потоке, чем ваш». Документация здесь:
http://developer.android.com/intl/de/reference/android/os/Handler.html
Обработчик должен быть создан в потоке, в котором он будет использоваться. Поэтому создание его в конструкторе потока, который выполняется потоком, создающим этот поток, не вариант.
Когда обработчик предназначен для потока, отличного от потока пользовательского интерфейса, также должен использоваться класс 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);
Есть ли лучшие решения? Спасибо.