I valori di ritorno da swing utilizzando invokeAndWait
-
18-09-2019 - |
Domanda
Sto usando il seguente approccio per creare componenti e restituire i valori da swing a / da fuori EDT. Per esempio, il seguente metodo potrebbe essere una proroga per JFrame
, per creare un JPanel
e aggiungerlo alla JFrame
genitore:
public JPanel threadSafeAddPanel() {
final JPanel[] jPanel = new JPanel[1];
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
jPanel[0] = new JPanel();
add(jPanel[0]);
}
});
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
}
return jPanel[0];
}
L'array 1-lunghezza locale è usato per trasferire il "risultato" dall'interno del Runnable
, che viene richiamato nel EDT. Beh, sembra "un po '" hacky, e così le mie domande:
- Ha senso? È qualcun altro fare qualcosa di simile?
- è la matrice 1 di lunghezza un buon modo per trasferire il risultato?
- C'è un modo più semplice per fare questo?
Soluzione
- L'ingestione di eccezioni senza nemmeno la registrazione: male !! - vi odio te stesso quando si arriva su una cosa del genere dopo un 2 ore di bug-caccia
- No, la matrice non è un buon modo; Per prima cosa, offre nessun metodo facile per il codice chiamante di attendere per il thread EDT per eseguire il
Runnable
prima di andare a prendere il risultato - C'è una classe progettata esplicitamente per questo genere di cose:
SwingWorker
Altri suggerimenti
Sebbene tale metodo può avere senso in alcune situazioni, sarà inutile la maggior parte del tempo.
Il motivo è che la creazione di più (se non tutti) i componenti saranno sempre verificarsi da EDT, a seguito di un'azione utente (voce di menu o il pulsante cliccato) che sono sempre eseguito dal EDT.
Nei casi in cui si hanno grandi lavori da eseguire prima di creare il vostro pannello e non si desidera bloccare l'EDT, allora si dovrebbe, come suggerito da qualcun altro, utilizzare SwingWorker o un quadro swing che offrono supporto per le attività di lunghi ( generalmente basati sulla SwingWorker internamente ogni caso, ma non necessariamente).
Per quanto riguarda la tua domanda 2, purtroppo, non si dispone di molti modi per farlo:
- utilizzare un array 1-oggetto come hai fatto tu, che è la soluzione più semplice, ma anche più brutto
- Creare una classe ItemHolder (vedi di seguito) che fa quasi lo stesso, richiede un po 'più di lavoro, ed è più pulita, a mio parere
- Ultimo, uso java.util.concurrent strutture (futuri e Callable); che sarebbe il cleaniest credo, ma anche che richiede il massimo sforzo
Ecco, semplificato, la classe ItemHolder:
public class ItemHolder<T> {
public void set(T item) {...}
public T get() {...}
private T item;
}
- a) Ha senso. b) Non che io sappia.
- Buono come qualsiasi.
- Crea le JPanel al di fuori della chiamata
invokeAndWait
// Questa linea aggiunto per placare Markdown
public JPanel threadSafeAddPanel() {
final JPanel jPanel = new JPanel();
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
add(jPanel);
}
});
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
}
return jPanel;
}
Si può facilmente verificare se il thread corrente è l'EDT e quindi eseguire in modo corretto e più semplicemente in quel contesto. Come per l'utilizzo della matrice finale per ottenere il valore di ritorno, che è il modo più semplice quando si deve utilizzare una classe interna anonima come questo.
public JPanel threadSafeAddPanel() throws InterruptedException,
InvocationTargetException {
if (EventQueue.isDispatchThread()) {
JPanel panel = new JPanel();
add(panel);
return panel;
} else {
final JPanel[] jPanel = new JPanel[1];
EventQueue.invokeAndWait(new Runnable() {
public void run() {
jPanel[0] = new JPanel();
add(jPanel[0]);
}
});
return jPanel[0];
}
}