views:

225

answers:

4

I'm trying to implement a Passive View based gui system in swing. Basically i want to keep my view implementation (the part that actually contains swing code) minimal, and do most of the work in my Presenter class. the Presenter should have no dependency on swing and also should "run the show", i.e. tell the view what to do and not vice versa.

I run into problems when dealing with long running tasks, and threads separation in general. I want GUI updates to run on the EDT, and the presenter logic to run on a different thread. If i want the presenter to update some part of the GUI it's pretty easy, i write something like that:

public interface View {
    void setText(String text);
}

public class Presenter {
    View view;
    ...
    public void setTextInVIew() {
        view.setText("abc");
    }
}

public class MyFrame implements View {
    JTextField textField;
    ...
    public void setText(final String text) {
        SwingUtilities.InvokeLater(new Runnable() {
            public void run() {
                textField.setText(text);
            }
        });
    }
}

However, when the GUI is to inform the presenter that some action has occurred, i want to switch out of the EDT in react to it in a different thread:

public class Presenter {
    ...
    public void buttonPressed() {
         // shouldn't run on EDT
    }
}

public class MyFrame implements View {
    JButton button;
    public MyFrame() {
        ...
        button.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                presenter.ButtonPressed();
            }
        });
    }
}

since the actionPerformed code is running from the EDT, so will the presenter.buttonPressed. I know swing has the concept of SwingWorker - running tasks in a different thread, however it looks like i'll have to insert swing code into my presenter, and the view is running the show. Any ideas how to solve this?

+2  A: 

you could do something like the following, which will keep your GUI code in place and simply perform the work to get out of the EDT:

 button.addActionListener(new ActionListener() {
        @Override public void actionPerformed(ActionEvent e) {
           SwingWorker sw = new SwingWorker() {
             public Object doInBackground(){
                 presenter.ButtonPressed();             
                 return null;
            }
          }; 
          sw.execute();
        }
    });
akf
I use a similar pattern as above, and the SwingWorker threads work as expected. +1
Luhar
As an optimization, I recommend using an executor instead of spawning a new thread for ever swing worker call.
Kevin Day
+2  A: 

You might be interested in the Task API to avoid all the boilerplate. Otherwise akf's solution looks fine (although no need to create a variable for SwingWorker, you can just new and execute an anonymous one).

JRL
Your doInBackground() method needs to return Void (not void) and hence you need to return something (e.g. null) after the call to presenter.ButtonPressed().
Adamski
@Adamski: you're right, removed.
JRL
JRL: You were still one line shorter than akf as you where calling .execute() rather than assigning the SwingWorker to a local variable.
Adamski
A: 

Another approach to the SwingWorker solution outlined by others is to use an event bus with thread affinity. I actually think that this might be the best option for the type of decoupling you are going for.

Check out: EventBus

there are other implementations of the bus architecture, but EventBus is popular.

-- update --

So EventBus is going to provide a very clean way of proxying from non-EDT to EDT (a lot nicer than tons of explicit calls to SwingUtilities.invokeLater() - but basically doing the same thing. Although an EventBus is able to bundle up many notifications and have them hit in a single EDT runnable, so performance will be better).

But this doesn't address the need to proxy events from the EDT and get them running on a worker thread. There is a ThreadSafeEventService class in EventBus that could probably be used as the foundation for such a beast - it could be coupled with an ExecutorService, for example, to process certain event registrations for certain listeners.

I guess the key in all this for me is that whatever solution you come up with, it should try to encapsulate the spinning on/off the EDT

BTW - What you are asking about here is really similar to Microsoft's Apartment threading model.

Kevin Day
do you have to explicitly do something to get this thread affinity? i followed EventBus getting started code (the Frame publishes changeEvent on button click, and the Presenter subscribes to this event), however the processing of the event in the Presenter is still done on the EDT
Asaf David
that's a good point. EventBus only does one way proxying (if an event originates off the EDT, it will be proxied onto the EDT automatically). It sounds like you need it to work in both directions (events originating on the EDT need to be delivered to listeners off the EDT). I'll update the post to expand on this a bit.
Kevin Day
A: 

OK - I've got another option for you: Spin

Ultimately, all of these solutions are proxying calls between threads. I think the goal is to find a solution that doesn't require gobs of boilerplate code on your end. You could, for example, wire all of your listeners so they check to see if they are on an appropriate worker thread, then proxy to an ExecutorService if not. But that's a major hassle. Much better to get that proxying occuring in the layer between your business and view objects - the binding/listener/whatever you want to call it layer.

Kevin Day