Как получить значение из диалога, когда в любом потоке? - PullRequest
2 голосов
/ 12 октября 2019

Интересно, это правильный способ получения PIN-кода из диалогового окна?

public static <T> T getFromGui(Supplier<T> supplier) {
    if (CN.isEdt()) return supplier.get();
    final Object[] holder = new Object[1];
    CN.callSeriallyAndWait(() -> holder[0] = supplier.get());
    @SuppressWarnings("unchecked") final T result = (T) holder[0];
    return result;
}

public static String askPin() {
    if (!CN.isEdt()) return getFromGui(() -> askPin(bankzugang));

    ...
    final boolean cont = Dialog.show(
        "Your PIN", BoxLayout.encloseY(pin, label), ok, cancel) == ok;
    return cont? pin.getText() : "";
}

Кажется, он работает (пробовал один раз), но я немного запутался в callSeriallyAndWait:это аналог SwingUtilities.invokeAndWait? В чем различия?

На самом деле мне нужно несколько диалогов для работы с каждым потоком, включая EDT. Есть ли лучший способ, чем добавить строку «getFromGui» к каждому из них, как указано выше?

1 Ответ

1 голос
/ 13 октября 2019

Да, callSeriallyAndWait является эквивалентом SwingUtilities.invokeAndWait. В этом нет логической разницы.

Это может сработать, но здесь есть несколько проблем ... Первое:

Что, если два потока одновременно показывают диалог, подобный этому. Первый будет отображать второй диалог, который может отображать первый диалог в бесконечном цикле ... Это может произойти, даже если вы физически находитесь на EDT, потому что код не является частью нормального потока приложения.

В отличие от Swing, у нас есть только один Form за раз, поэтому диалог «возвращается», и это может быть сложно.

Итак, прежде всего ваш код GUI должен проверить, что вы нев настоящее время запрашивается пин-код, и лучший способ сделать это - иметь один класс, отвечающий за получение пин-кода от пользователя, если это необходимо, и он должен иметь статическое состояние. Если также следует проверить, что в это время нет другого диалогового окна, и если да, то может не показываться, например:

if(getCurrentForm() instanceof Dialog) {
   // ... don't show yet
}

Если я правильно понимаю вашу проблему, у вас есть фоновые потоки, которые могут нуждаться в булавке. В этом случае более одного потока могут быть перехвачены без булавки, и это потребуется из графического интерфейса. Таким образом, оба потока должны быть приостановлены, но только один из них должен вызывать callSeriallyAndWait ... Это означает, что текущий подход в любом случае не соответствует требованиям.

Обычно мы избегаем callSeriallyAndWait, поскольку он намного медленнее (на Swing тоже). Я бы также использовал InteractionDialog или аналогичный, который менее навязчив, чем обычный диалог. Тем не менее, это не модально. Но в этом случае это не должно иметь значения.

Вам необходимо разработать собственный код блокировки потоков для фоновых потоков, и он может реагировать на стандартный обратный вызов, чтобы освободить все ожидающие фоновые потоки. Затем вы можете просто использовать любой код, который вы хотите создать в GUI, и использовать любой слушатель, затем обновить общее состояние в классе с помощью нового вывода и вызвать notifyAll(), чтобы разбудить потоки, ожидающие вывода. Например,

synchronized(LOCK) {
     if(pin == null && !dialogIsShowing) {
         dialogIsShowing = true;
         callSerially(() -> promptForPin());
     }
     while(pin == null) {
         LOCK.wait();
     }
}

Тогда логика интерфейса:

private void onUserSubmittedPin(String pin) {
     synchronized(LOCK) {
         this.pin = pin;
         dialogIsShowing = false;
         LOCK.notifyAll();
     }
}
...