How to get a value from a dialog when on any thread?

核能气质少年 提交于 2021-01-28 02:10:14

问题


I wonder if this is the right way for getting a PIN from a dialog?

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() : "";
}

It seems to work (tried once), but I'm a bit confused about callSeriallyAndWait: Is it the analogon of SwingUtilities.invokeAndWait? What are the differences?

Actually, I need a few dialogs to work from every thread, including EDT. Is there a better way than adding the "getFromGui" line to each of them as above?


回答1:


Yes, callSeriallyAndWait is the equivalent of SwingUtilities.invokeAndWait. There's no logical difference.

This can work but there's a couple of concerns here... First:

What if two threads show a dialog like that at once. The first will dispose to show the second dialog which might dispose to show the first dialog in an infinite loop... This can happen even if your physically on the EDT because the code isn't a part of the normal application flow.

Unlike Swing we have only one Form at a time so a dialog "goes back" and that can get tricky.

So first of all your GUI code needs to check that you're not currently asking for a pin and the best way to do that is to have one class responsible for getting the pin from the user if necessary and it should have a static state. If should also check that there's no other dialog showing at this time and if so it might want to avoid showing e.g.:

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

If I understand your problem correctly you have background threads that might need a pin. In that case more than one thread might be caught without a pin and would need it from the GUI. So both threads need to be suspended but only one of them should invoke callSeriallyAndWait... This means the current approach is sub par anyway.

Normally we avoid callSeriallyAndWait as it's much slower (on Swing too). I would also use an InteractionDialog or similar which is less intrusive than a regular dialog. However, it isn't modal. But in this case it shouldn't matter.

You need to develop your own thread blocking code for the background threads and it can react to a standard callback to release all the waiting background threads. You can then just use any code you want to create the GUI and use any listener then update shared state in the class with the new pin and invoke notifyAll() to wake up threads waiting for the pin. E.g.

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

Then the UI logic:

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


来源:https://stackoverflow.com/questions/58351629/how-to-get-a-value-from-a-dialog-when-on-any-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!