Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Im Model werden events gefeuert auf die in der GUI gehorcht werden. Nun gibt es aber bei den background Jobs/Threads Probleme, da diese nicht zum EDT synchronisiert werden. Wie kann ich das am besten machen. Ich würde ungern alle GUI Klassen anfassen und in den Listener ein
Code:
if(SwingUtilities.isEventDispatchThread())
und je nachdem synchronisieren oder den ablauf fortsetzen. Und in den Models will ich keine Abhängigkeit zu Swing also kommt es beim feuern gar nicht in Frage. Gibt es hierfür ein geeignetes Pattern, eine beste Practice oder hat jemand so eine gute Idee?
- Das Modell feuert alle Events pauschal auf dem EDT
Du meinst, das "kommt nicht in Frage", und in den meisten Fällen stimmt das wohl - ich erwähne es eher der Vollständigkeit halber, weil es in bestimmten Kontexten Sinn machen KÖNNTE
- Das GUI verarbeitet alle einkommenden Events Thread-Safe
D.h. das GUI kann auf jedem Thread benachrichtigt werden, und kümmert sich selbst darum, dass "seine" Swing-Components nur auf dem EDT verändert werden (ggf. mit isEventDispatchThread-Abfrage)
- Man muss sich beim Herstellen der Verbindung zwischen Model und View um diese Dinge kümmern
Genau wie man ein Swing-Modell nicht auf jedem Thread verändern darf, darf man das ggf. bei "eigenen" Modell-Klassen auch nicht. Es ist Sache desjenigen, der das ganze verwendet, sicherzustellen, dass alles passt....
- Man erstellt "Proxy"-Klassen, die keine andere Funktion haben, als die Threadsicherheit herzustellen.
Das klingt erstmal komisch. IST es vielleicht auch Das Potential der dynamischen Proxy-Klassen ist mit erst kürzlich andeutungsweise bewußt geworden. Tatsächlich muss man da in erster Näherung nur EINE Zeile einfügen (hier mit [c]// It's a kind of magic...[/c] markiert). Für die praktische Anwendung müßte man sich da noch mehr überlegen, aber... ich wollt' das halt grad mal ausprobieren ...
Java:
// Marco13 for [url]http://www.java-forum.org/awt-swing-swt/94565-model-listener-background-jobs.html#post600770[/url]
import java.lang.reflect.*;
import javax.swing.*;
public class SwingProxyTest extends JFrame implements ExampleListener
{
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
SwingProxyTest s = new SwingProxyTest();
s.setVisible(true);
ExampleListener listener = s;
// It's a kind of magic...
listener = SwingProxyFactory.createProxy(ExampleListener.class, s);
ExampleModel exampleModel = new ExampleModel(listener);
exampleModel.start();
}
});
}
private JTextArea textArea;
public SwingProxyTest()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textArea = new JTextArea();
getContentPane().add(textArea);
setSize(500,500);
}
@Override
public void fireEvent(String message)
{
textArea.append(message+" from "+Thread.currentThread()+"\n");
}
}
class ExampleModel implements Runnable
{
private ExampleListener exampleListener;
public ExampleModel(ExampleListener exampleListener)
{
this.exampleListener = exampleListener;
}
public void start()
{
Thread t = new Thread(this);
t.start();
}
public void run()
{
while (true)
{
exampleListener.fireEvent(""+System.currentTimeMillis());
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
interface ExampleListener
{
void fireEvent(String message);
}
class SwingProxyFactory
{
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<?> c, final T delegate)
{
return (T)java.lang.reflect.Proxy.newProxyInstance(
c.getClassLoader(),
new Class<?>[]{c},
new InvocationHandler()
{
public Object invoke(Object proxy, final Method method, final Object[] args)
throws Throwable
{
if (SwingUtilities.isEventDispatchThread())
{
return method.invoke(delegate, args);
}
if (method.getReturnType().equals(void.class))
{
// Could be blocking with invokeAndWait,
// currently it's non-blocking
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
method.invoke(delegate, args);
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
}
});
return null;
}
else
{
final Object result[] = new Object[1];
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
try
{
result[0] = method.invoke(delegate, args);
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
}
});
return result[0];
}
}
});
}
}
- Das Modell feuert alle Events pauschal auf dem EDT
Du meinst, das "kommt nicht in Frage", und in den meisten Fällen stimmt das wohl - ich erwähne es eher der Vollständigkeit halber, weil es in bestimmten Kontexten Sinn machen KÖNNTE
- Das GUI verarbeitet alle einkommenden Events Thread-Safe
D.h. das GUI kann auf jedem Thread benachrichtigt werden, und kümmert sich selbst darum, dass "seine" Swing-Components nur auf dem EDT verändert werden (ggf. mit isEventDispatchThread-Abfrage)
- Man muss sich beim Herstellen der Verbindung zwischen Model und View um diese Dinge kümmern
Genau wie man ein Swing-Modell nicht auf jedem Thread verändern darf, darf man das ggf. bei "eigenen" Modell-Klassen auch nicht. Es ist Sache desjenigen, der das ganze verwendet, sicherzustellen, dass alles passt....
- Man erstellt "Proxy"-Klassen, die keine andere Funktion haben, als die Threadsicherheit herzustellen.
Das klingt erstmal komisch. IST es vielleicht auch Das Potential der dynamischen Proxy-Klassen ist mit erst kürzlich andeutungsweise bewußt geworden. Tatsächlich muss man da in erster Näherung nur EINE Zeile einfügen (hier mit [c]// It's a kind of magic...[/c] markiert). Für die praktische Anwendung müßte man sich da noch mehr überlegen, aber... ich wollt' das halt grad mal ausprobieren ...
Java:
// Marco13 for [url]http://www.java-forum.org/awt-swing-swt/94565-model-listener-background-jobs.html#post600770[/url]
import java.lang.reflect.*;
import javax.swing.*;
public class SwingProxyTest extends JFrame implements ExampleListener
{
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
SwingProxyTest s = new SwingProxyTest();
s.setVisible(true);
ExampleListener listener = s;
// It's a kind of magic...
listener = SwingProxyFactory.createProxy(ExampleListener.class, s);
ExampleModel exampleModel = new ExampleModel(listener);
exampleModel.start();
}
});
}
private JTextArea textArea;
public SwingProxyTest()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textArea = new JTextArea();
getContentPane().add(textArea);
setSize(500,500);
}
@Override
public void fireEvent(String message)
{
textArea.append(message+" from "+Thread.currentThread()+"\n");
}
}
class ExampleModel implements Runnable
{
private ExampleListener exampleListener;
public ExampleModel(ExampleListener exampleListener)
{
this.exampleListener = exampleListener;
}
public void start()
{
Thread t = new Thread(this);
t.start();
}
public void run()
{
while (true)
{
exampleListener.fireEvent(""+System.currentTimeMillis());
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
interface ExampleListener
{
void fireEvent(String message);
}
class SwingProxyFactory
{
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<?> c, final T delegate)
{
return (T)java.lang.reflect.Proxy.newProxyInstance(
c.getClassLoader(),
new Class<?>[]{c},
new InvocationHandler()
{
public Object invoke(Object proxy, final Method method, final Object[] args)
throws Throwable
{
if (SwingUtilities.isEventDispatchThread())
{
return method.invoke(delegate, args);
}
if (method.getReturnType().equals(void.class))
{
// Could be blocking with invokeAndWait,
// currently it's non-blocking
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
method.invoke(delegate, args);
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
}
});
return null;
}
else
{
final Object result[] = new Object[1];
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
try
{
result[0] = method.invoke(delegate, args);
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
}
});
return result[0];
}
}
});
}
}
Punkt 1 und 2 wollte ich ja verhindern...
Punkt 3 find ich auch nicht so prickelnd.
Punkt 4 müsste ich mit mal genauer anschauen... Was ich nicht versteh warum unterscheidest du zwischen void (invokeLater) und welche die einen Rückgabwert(invokeAndWait) haben?
Punkt 1 und 2 wollte ich ja verhindern...
Punkt 3 find ich auch nicht so prickelnd.
Punkt 4 müsste ich mit mal genauer anschauen... Was ich nicht versteh warum unterscheidest du zwischen void (invokeLater) und welche die einen Rückgabwert(invokeAndWait) haben?
Hmja, du hattest ja gesagt, dass 1 und 2 nicht so toll sind, und das stimmt ja auch, aber ... mal ganz objektiv-pragmatisch: Man hat drei ... "Entitäten":
- Ein Modell
- Eine View
- Einen Controller
Modell und View haben unterschiedliche Threadanforderungen. Die Erfüllung dieser Anforderungen kann ja nun nur an drei Stellen sichergestellt werden:
- Im Modell
- In der View
- Im Controller
Wenn man jetzt (was ich noch am ehesten als "pauschal berechtigt" ansehen würde) das Modell für diese Zwecke ausschließt, und diese Dinge in der View zumindest "gerne verhindern" würde, wird die Luft eben langsam dünn...
Wenn man also... (für sich selbst und seinen eigenen, konkreten Anwendungfall) zu dem Schluss kommt, dass die Threadfragen vom Controller geklärt werden "müssen", dann "muss" das eben so sein, auch wenn's "nicht so prickelnd" ist. DANN geht es nur noch um die Frage, WIE man das genau macht.
Die Sache mit den Proxies war eher eine ... "verrückte Idee", also... das sollte man nicht so unüberlegt einbauen, aber... ich finde den Ansatz (wegen seiner Einfachheit und Flexibilität) eben ganz interessant.... Kann gut sein, dass ich das mal irgendann aufbohre.
Der Unterschied zwischen "void" und "nicht void" ist (wie ja auch durch den (einzigen ) Kommentar angdeutet ist) ein bißchen diffizil. Wenn eine Methode einen Wert zurückliefern soll, dann MUSS sie mit InvokeAndWait aufgerufen werden - andernfalls kommt man ja nicht an den Rückgabewert. Listener-Methoden sind aber ... eigentlich IMMER (!? ???:L ?!) void-Methoden, d.h. da braucht man eigentlich nicht zu warten. Wenn aber ein Modell z.B. 1000 Events pro Sekunde feuert/feuern will, und die Abarbeitung jedes Events in der GUI 1 Sekunde dauert, dann dürfte die Swing-Event-Queue ziemlich schnell vollaufen. "Sicherer" um das zu verhindern wäre eigentlich, IMMER invokeAndWait zu verwenden - allerdings bremst ein langsames GUI dann u.U. den Modell-Thread aus (je nachdem, wie viele Events der wirft...). Ausgefeiltere Methoden, um das zu verhindern (irgendwelches Coalescing oder so) müßte man sich halt noch überlegen...
Ganz nebenbei: Der Fall, dass auch auf das Modell nur in einem bestimmten Thread zugegriffen werden darf, kann ja auch auftreten... Das sollte ggf. auch berücksichtigt werden...
Hmja, du hattest ja gesagt, dass 1 und 2 nicht so toll sind, und das stimmt ja auch, aber ... mal ganz objektiv-pragmatisch: Man hat drei ... "Entitäten":
- Ein Modell
- Eine View
- Einen Controller
Modell und View haben unterschiedliche Threadanforderungen. Die Erfüllung dieser Anforderungen kann ja nun nur an drei Stellen sichergestellt werden:
- Im Modell
- In der View
- Im Controller
Wenn man jetzt (was ich noch am ehesten als "pauschal berechtigt" ansehen würde) das Modell für diese Zwecke ausschließt, und diese Dinge in der View zumindest "gerne verhindern" würde, wird die Luft eben langsam dünn...
Wenn man also... (für sich selbst und seinen eigenen, konkreten Anwendungfall) zu dem Schluss kommt, dass die Threadfragen vom Controller geklärt werden "müssen", dann "muss" das eben so sein, auch wenn's "nicht so prickelnd" ist. DANN geht es nur noch um die Frage, WIE man das genau macht.
Die Sache mit den Proxies war eher eine ... "verrückte Idee", also... das sollte man nicht so unüberlegt einbauen, aber... ich finde den Ansatz (wegen seiner Einfachheit und Flexibilität) eben ganz interessant.... Kann gut sein, dass ich das mal irgendann aufbohre.
Der Unterschied zwischen "void" und "nicht void" ist (wie ja auch durch den (einzigen ) Kommentar angdeutet ist) ein bißchen diffizil. Wenn eine Methode einen Wert zurückliefern soll, dann MUSS sie mit InvokeAndWait aufgerufen werden - andernfalls kommt man ja nicht an den Rückgabewert. Listener-Methoden sind aber ... eigentlich IMMER (!? ???:L ?!) void-Methoden, d.h. da braucht man eigentlich nicht zu warten. Wenn aber ein Modell z.B. 1000 Events pro Sekunde feuert/feuern will, und die Abarbeitung jedes Events in der GUI 1 Sekunde dauert, dann dürfte die Swing-Event-Queue ziemlich schnell vollaufen. "Sicherer" um das zu verhindern wäre eigentlich, IMMER invokeAndWait zu verwenden - allerdings bremst ein langsames GUI dann u.U. den Modell-Thread aus (je nachdem, wie viele Events der wirft...). Ausgefeiltere Methoden, um das zu verhindern (irgendwelches Coalescing oder so) müßte man sich halt noch überlegen...
Ganz nebenbei: Der Fall, dass auch auf das Modell nur in einem bestimmten Thread zugegriffen werden darf, kann ja auch auftreten... Das sollte ggf. auch berücksichtigt werden...