Вызов Looper более одного раза вызывает "отправку сообщения обработчику в мертвой ветке"
Я использую Executor [исправленный пул потоков] с моей собственной ThreadFactory, которая добавляет Looper:
Handler HANDLER = new Handler();
Executor THREADS = Executors.newFixedThreadPool(THREAD_POOL_SIZE, new ThreadFactory() {
@Override public Thread newThread(Runnable runnable) {
return new MyThread(new Runnable() {
@Override public void run() {
Looper.prepare();
runnable.run();
}
});
}
});
private static class MyHandler extends Handler {
public boolean fail;
public void handleMessage(Message msg) {
switch(msg.what) {
case 1:
this.fail = msg.arg1 == 1;
Looper.myLooper().quit();
break;
}
}
}
}
Я запускаю поток, который делает сетевые запросы, но если сеть не работает, я бы хотел, чтобы диалоговое сообщение отображалось пользователю. Этот процесс довольно сложный, так как требует выполнения И отображения запроса в потоке пользовательского интерфейса. Я могу дождаться ответа пользователя на диалоговое окно, просто добавив цикл в сетевой поток, и дождусь отправки сообщения из потока пользовательского интерфейса. Это позволяет мне инкапсулировать сетевые запросы в потоке while(tryAgain). Все работает хорошо, за исключением случаев, когда метод Looper.loop() вызывается во второй раз (после отображения второго диалогового окна с ошибкой в сети) и сообщение отправляется диалогом (в потоке пользовательского интерфейса) обработчику сетевого потока:
THREADS.execute(new Runnable() {
private MyHandler myHandler = new MyHandler();
@Override public void run() {
boolean tryAgain = true;
while(tryAgain) {
try {
switch(request) {
[Handle network requests]
}
tryAgain = false;
} catch(IOException e) {
// The network is unavailable. Ask the user if we should try again.
e.printStackTrace();
} finally {
if(tryAgain) {
HANDLER.post(new Runnable() { // The UI thread
@Override public void run() {
theAlertDialog.show();
}
});
// Wait for the results from the dialog which lives in the UI thread.
Looper.loop();
// At this point the dialog has informed us of our answer.
tryAgain = !myHandler.fail;
}
}
}
}
});
В экземпляре AlertDialog есть OnClickListener:
DialogInterface.OnClickListener myOnclickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Message msg = myHandler.obtainMessage(1);
msg.setTarget(this.handler);
msg.sendToTarget();
}
}
Я проверил, что поток все еще активен с handler.getLooper().getThread().isAlive()
который всегда возвращает истину, но все равно дает мне "отправку сообщения обработчику в мертвой ветке". Как получается, что Message/Handler решил, что поток мертв? Разве он не должен полагаться на метод.isAlive()? В конце я пытаюсь избежать репликации сборки управления потоками в ОС Android:-)
2 ответа
Если вы проверите источник в Android/ OS /MessageQueue.java, вы можете увидеть что-то вроде следующего
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
}
Таким образом, очередь сообщений в основном непригодна после первого вызова Looper.quit(), поскольку она ставит в очередь Сообщение с нулевой целью, которая является магическим идентификатором для очереди сообщений, чтобы прекратить ставить в очередь и выглядеть "мертвой".
См. http://code.google.com/p/android/issues/detail?id=20915, которая является возможной основной причиной проблемы. Это включает обходной путь для проблемы.