开发者

Swing, Passive View and Long running tasks

开发者 https://www.devze.com 2022-12-21 14:09 出处:网络
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 P

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;
    .开发者_如何学Go..
    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?


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


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).


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消