Java: как запустить диалог пользовательского интерфейса из другого потока, например, для Authenticator

Моя проблема в двух словах: мое приложение с графическим интерфейсом должно выполнять длительную сетевую загрузку. Загрузка осуществляется в отдельном потоке. Возможно, что удаленный сайт потребует аутентификации, поэтому я хочу определить Authenticator, который открывает диалоговое окно "введите ваше имя пользователя и пароль". Я понимаю, что этот диалог должен быть запущен из потока пользовательского интерфейса.

Я уверен, что я не первый человек, который делает это. Какова лучшая практика для того, чтобы фоновый поток запускал диалог в потоке пользовательского интерфейса и блокировал, пока этот диалог не был закрыт?

ps фоновый поток очень большой и делает гораздо больше, чем просто скачивает файл из сети. Другими словами, на данный момент, вероятно, нецелесообразно конвертировать его в SwingWorker, и в любом случае, я не уверен, как бы я решил эту проблему с помощью SwingWorker.

3 ответа

Решение

Вам нужен SwingUtlities.invokeLater для представления диалогового окна и объект синхронизации / уведомления для "приостановки" и ожидания ответа пользователя.

В основном в вашей рабочей (не GUI) теме:

final Object obj = new Object() ; // or something to receive your dialog's answer(s)
Runnable r = new Runnable() {

   void run() {
     Dialog d = new Dialog() ;

     Button b = new JButton("ok") ;
     b.addActionListener(new ActionListener() {
         void actionPerformed(ActionEvent e) {
             synchronize(obj) { // can lock when worker thread releases with wait
                obj.notify() ; // signals wait
             }
         }
     }) ;

   }
} ;

synchronize( obj ) {
   SwingUtilites.invokeLater(r) ; // executs r's run method on the swing thread
   obj.wait() ; // releases obj until worker thread notifies
}

Эдвард Фальк написал Actually, it looks like invokeLater() will also do what I want

нет, это неправильно, потому что вы должны рассчитать, что EDT существует, и SwingUtilites.invokeLater() работает, если там работает EDT, если нет, то SwingUtilites.invokeLater() ничего не уведомляет, любое всплывающее окно будет отображаться, может быть, просто пустой прямоугольник

1/ создать EDT с помощью java.swing.Action

2 / отладить эту идею trashgod Я думаю, что эта логика является правильной и лучше всего для этого

Для протокола, вот мое окончательное решение, основанное на ответе Эндрю:

final Authenticator authenticator =
   new Authenticator() {
        @Override
        public PasswordAuthentication getPasswordAuthentication() {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    public void run() {
                        // Launch the GUI dialog
                        queryUsernamePassword(getRequestingHost(), getRequestingPrompt());
                    }
                });
                if (username == null)
                    return null;
                return new PasswordAuthentication(username,
                            password.toCharArray());
            } catch (Exception e) {
                return null;
            }
        }
    };
Другие вопросы по тегам