簡體   English   中英

如何將SwingWorker的發布委托給其他方法

[英]How to delegate SwingWorker's publish to other methods

我的“問題”可以用以下描述。 假設我們有一個密集的過程,我們希望在后台運行並讓它更新一個Swing JProgress欄。 解決方案很簡單:

import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;


/**
 * @author Savvas Dalkitsis
 */
public class Test {

    public static void main(String[] args) {
        final JProgressBar progressBar = new JProgressBar(0,99);
        SwingWorker<Void, Integer> w = new SwingWorker<Void, Integer>(){

            @Override
            protected void process(List<Integer> chunks) {
                progressBar.setValue(chunks.get(chunks.size()-1));
            }

            @Override
            protected Void doInBackground() throws Exception {

                for (int i=0;i<100;i++) {
                    publish(i);
                    Thread.sleep(300);
                }

                return null;
            }

        };
        w.execute();
        JOptionPane.showOptionDialog(null,
                new Object[] { "Process", progressBar }, "Process",
                JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
                null, null, null);
    }

}

現在假設我有各種方法需要很長時間。 例如,我們有一個從服務器下載文件的方法。 或者上傳到服務器的另一個。 或任何真的。 將發布方法委派給這些方法的正確方法是什么,以便他們可以適當地更新GUI?

到目前為止我發現的是(假設方法“aMethod”駐留在其他一些包中):

import java.awt.event.ActionEvent;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;


/**
 * @author Savvas Dalkitsis
 */
public class Test {

    public static void main(String[] args) {
        final JProgressBar progressBar = new JProgressBar(0,99);
        SwingWorker<Void, Integer> w = new SwingWorker<Void, Integer>(){

            @Override
            protected void process(List<Integer> chunks) {
                progressBar.setValue(chunks.get(chunks.size()-1));
            }

            @SuppressWarnings("serial")
            @Override
            protected Void doInBackground() throws Exception {

                aMethod(new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        publish((Integer)getValue("progress"));
                    }
                });

                return null;
            }

        };
        w.execute();
        JOptionPane.showOptionDialog(null,
                new Object[] { "Process", progressBar }, "Process",
                JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
                null, null, null);
    }

    public static void aMethod (Action action) {
        for (int i=0;i<100;i++) {
            action.putValue("progress", i);
            action.actionPerformed(null);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

它有效,但我知道它缺乏一些東西。 有什么想法嗎?

(我正在更新我的答案,使其更清晰和概括)

雖然您已經成功地解耦了邏輯和表示,但它並沒有以適合代碼重用的方式完成。 Java的PropertyChangeSupport通過實現綁定屬性 ,可以輕松地將邏輯與表示分離,並獲得一些實質性的重用。 我們的想法是使用事件處理程序而不是操作對象。

首先,概念化抽象。 后台工作需要間歇性地“喊出”(發布)到GUI,並且GUI需要監聽它。 兩個通用類將編寫這個想法:

/**
 * Wrapper for the background logic.
 *
 * <T> return type
 * <S> intermediary type (the "shout out")
 */
public static abstract class LoudCall<T, S> implements Callable<T> {

    private PropertyChangeSupport pcs;
    private S shout;

    public LoudCall() {
        pcs = new PropertyChangeSupport(this);
    }

    public void shoutOut(S s) {
        pcs.firePropertyChange("shoutOut", this.shout, 
                this.shout = s);
    }

    public void addListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void removeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

    @Override
    public abstract T call() throws Exception;
}

/**
 * Wrapper for the GUI listener.
 *
 * <T> return type
 * <S> intermediary type (the "shout out" to listen for)
 */
public static abstract class ListenerTask<T, S> extends SwingWorker<T, S> 
        implements PropertyChangeListener {

    private LoudCall<T, S> aMethod;

    public ListenerTask(LoudCall<T, S> aMethod) {
        this.aMethod = aMethod;
    }

    @Override
    protected T doInBackground() throws Exception {
        aMethod.addListener(this);
        return aMethod.call();
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if ("shoutOut".equals(evt.getPropertyName())) {
            publish((S)evt.getNewValue());
        }
    }

    @Override
    protected abstract void process(List<S> chunks);
}

這些類可用於所有Swing小部件。 對於ProgressBar,“shout out”將是一個Integer,返回類型為Void:

public class ProgressExample {  
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {

        // 1. setup the progress bar
        final JProgressBar progressBar = new JProgressBar(0, 99);

        // 2. Wrap the logic in a "Loud Call"
        LoudCall<Void, Integer> aMethod = new LoudCall<Void, Integer>() {
            @Override
            public Void call() throws Exception {
                for (int i = 0; i < 100; i++) {
                    // "i have an update for the GUI!"
                    shoutOut(i);
                    Thread.sleep(100);
                }
                return null;
            }
        };

        // 3. Run it with a "Listener Task"
        (new ListenerTask<Void, Integer>(aMethod) {
            @Override
            protected void process(List<Integer> chunks) {
                progressBar.setValue(chunks.get(chunks.size() - 1));
            }
        }).execute();

        // 4. show it off!
        JOptionPane.showOptionDialog(null,
            new Object[] { "Process", progressBar }, "Process",
            JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
            null, null, null
        );
    }
        });
    }
}

只有收聽者需要了解有關GUI細節的任何信息,並且后台邏輯仍然可以控制發布(間接地,通過“喊叫”)。 此代碼更簡潔,可讀且可重用。

我意識到這個問題現在已經很老了,但希望它對某人有所幫助!

也許為每個長方法做一個SwingWorker。 每個SwingWorker都有自己的進度級別。

每個SwingWorker在doInBackground方法期間更新它自己的進度級別,然后調用publish。 在流程方法內部,所以在EDT內部,每個SwingWorker都會讀取它的進度級別,並更新模型和願景的一般進度條。

我遇到了類似的問題。 這是我發現的,也許真正正確的答案缺乏,但讓我們嘗試一下:

  • 如果我們有很多迭代,我們可以在doInBackGround()方法中更新進度條。 JProgressBar是SwingWorker的構造函數參數,它擴展了SwingWorker(所以是的,我們使用自定義)
  • 如果我們沒有迭代並且我們不能中斷需要花費大量時間來完成的方法,我們可以將整個事情搞砸並像大多數人一樣(因此我們的進度條沒有線性過程但是它只是在部分之后刷新它的值工作完成了)。 壞消息是,如果我們的方法是唯一的工作人員(在背景上發送電子郵件),進度條將突然變滿 雖然不是很好,但讓我們來看看第三個選項
  • 這可能是相當瘋狂和性能減慢,這都是因為我們的應用程序必須是花哨的 那么讓我們來看一下需要很長時間才能完成的方法的源代碼。 我們覆蓋它並粘貼完全相同的代碼,但我們再添加一個參數 - 猜猜是什么,是JProgressBar。 在方法內部,我們創建了Thread,它將運行直到某個布爾參數(指示方法的標志最終完成)設置為true。 線程將在一些合理的時間間隔內不斷更新JProgressBar。 最大的問題是假設,合理的間隔是多少。 我們應該進行一些測試並估計間隔值。

在第三點中,我描述了如何從方法執行Thread,該方法完成了一些非迭代的任務(至少在我們的Java代碼中沒有)並且不能被中斷。 線程更新作為方法參數給出的JProgressBar。 然而,這是純方法調用的定義較慢

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM