【问题标题】:Java SwingWorker with JDialog showing JProgressBar during JDBC network operation带有 JDialog 的 Java SwingWorker 在 JDBC 网络操作期间显示 JProgressBar
【发布时间】:2013-08-24 13:59:30
【问题描述】:

我有一个带有按钮的框架,当按下它时会显示带有进度条的JDialog,并且正在使用 jdbc 驱动程序获取一些数据(正在更新进度条)。我需要一个取消按钮,所以我花了一些时间弄清楚如何连接所有东西。它似乎有效,但我真的不确定这种方式是否有好处。如果有人有空闲时间,请检查此代码并告诉我它是否有任何问题 - 主要是整个 SwingWorker 和取消的东西。

在我的电脑 (linux) 上,不成功的网络连接尝试(someNetworkDataFetching 方法)需要一整分钟才能超时,当我尝试创建时,我是否必须担心仍在工作的 SwingWorkers(尽管被取消但仍在等待连接)新的?

注意:你需要mysql jdbc驱动库来运行这段代码。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Test extends JFrame {

    private JProgressBar progressBar = new JProgressBar();
    private JLabel label = new JLabel();
    private DataFetcherProgress dfp;

    /**
     * This class holds retrieved data.
     */
    class ImportantData {

        ArrayList<String> chunks = new ArrayList<>();

        void addChunk(String chunk) {
            // Add this data 
            chunks.add(chunk);
        }
    }

    /**
     * This is the JDialog which shows data retrieval progress.
     */
    class DataFetcherProgress extends JDialog {

        JButton cancelButton = new JButton("Cancel");
        DataFetcher df;

        /**
         * Sets up data fetcher dialog.
         */
        public DataFetcherProgress(Test owner) {
            super(owner, true);
            getContentPane().add(progressBar, BorderLayout.CENTER);
            // This button  cancels the data fetching worker.
            cancelButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    df.cancel(true);

                }
            });
            getContentPane().add(cancelButton, BorderLayout.EAST);
            setLocationRelativeTo(owner);
            setSize(200, 50);
            df = new DataFetcher(this);
        }

        /**
         * This executes data fetching worker.
         */
        public void fetchData() {
            df.execute();
        }
    }

    class DataFetcher extends SwingWorker<ImportantData, Integer> {

        DataFetcherProgress progressDialog;

        public DataFetcher(DataFetcherProgress progressDialog) {
            this.progressDialog = progressDialog;
        }

        /**
         * Update the progress bar.
         */
        @Override
        protected void process(List<Integer> chunks) {
            if (chunks.size() > 0) {
                int step = chunks.get(chunks.size() - 1);
                progressBar.setValue(step);
            }
        }

        /**
         * Called when worker finishes (or is cancelled).
         */
        @Override
        protected void done() {
            System.out.println("done()");
            ImportantData data = null;
            try {
                data = get();
            } catch (InterruptedException | ExecutionException | CancellationException ex) {
                System.err.println("done() exception: " + ex);
            }
            label.setText(data != null ? "Retrieved data!" : "Did not retrieve data.");
            progressDialog.setVisible(false);
        }

        /**
         * This pretends to do some data fetching.
         */
        private String someNetworkDataFetching() throws SQLException {
            DriverManager.getConnection("jdbc:mysql://1.1.1.1/db", "user", "pass");
            // Retrieve data...
            return "data chunk";
        }

        /**
         * This tries to create ImportantData object.
         */
        @Override
        protected ImportantData doInBackground() throws Exception {
            // Show the progress bar dialog.
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    dfp.setVisible(true);
                }
            });

            ImportantData data = new ImportantData();
            try {
                int i = 0;
                // There is a network operation here (JDBC data retrieval)
                String chunk1 = someNetworkDataFetching();
                if (isCancelled()) {
                    System.out.println("DataFetcher cancelled.");
                    return null;
                }
                data.addChunk(chunk1);
                publish(++i);

                // And another jdbc data operation....
                String chunk2 = someNetworkDataFetching();
                if (isCancelled()) {
                    System.out.println("DataFetcher cancelled.");
                    return null;
                }
                data.addChunk(chunk2);
                publish(++i);
            } catch (Exception ex) {
                System.err.println("doInBackground() exception: " + ex);
                return null;
            }
            System.out.println("doInBackground() finished");
            return data;
        }
    }

    /**
     * Set up the main window.
     */
    public Test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        getContentPane().add(label, BorderLayout.CENTER);
        // Add a button starting data fetch.
        JButton retrieveButton = new JButton("Do it!");
        retrieveButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fetchData();
            }
        });
        getContentPane().add(retrieveButton, BorderLayout.EAST);
        setSize(400, 75);
        setLocationRelativeTo(null);
        progressBar.setMaximum(2);
    }

    // Shows new JDialog with a JProgressBar and calls its fetchData()
    public void fetchData() {
        label.setText("Retrieving data...");
        dfp = new DataFetcherProgress(this);
        dfp.fetchData();
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    // Use jdbc mysql driver
                    Class.forName("com.mysql.jdbc.Driver");
                } catch (ClassNotFoundException ex) {
                    ex.printStackTrace();
                    return;
                }

                // Show the Frame
                new Test().setVisible(true);
            }
        });

    }
}

【问题讨论】:

    标签: java swing jdbc swingworker


    【解决方案1】:

    关于我唯一可能做的不同的事情是不要在 doInBackground 方法中使用 SwingUtilities.invokeLater 来显示对话框,但可能会使用 PropertyChangeListener 来监视对 state 属性工作者的更改。

    我还将使用PropertyChangeListener 来监视对worker 的progress 属性的更改。我将使用setProgress 方法(以及PropertyChangeListener 中的getProgress)而不是使用publish 来指示进度变化

    例如...java swingworker thread to update main Gui

    我还可以考虑在 JPanel 上创建 UI 并将其添加到 JDialog 而不是从 JDialog 扩展目录,因为如果您愿意,它会给以其他方式重用面板的机会...

    【讨论】:

    • 难道没有get(),否则所有异常都烟消云散
    • @mKorbel OP 在done 方法中调用get SwingWorker 的实现
    • 我认为(我的问题的原因)不要将返回与可能/等待异常混为一谈,请不要攻击,不要故意,只是我有一些错误反对使用 SwingWorker 的另一个原因是“好吧这种方式也有可能”,因为是关于遮蔽管,你把东西放在一边等待(像白痴一样:-) 在另一边等待一些预期的输出,还是我错了......
    • @mKorbel 我也不是SwingWorker 的忠实粉丝,它有它的位置。是的,get 是一个阻塞方法,它会一直等到doInBackground 完成,但是因为OP 在done 方法中使用了get,所以它会立即返回,因为结果是可用的。它由CallableFuture 或其他东西支持:P
    • 感谢您的关注。我仍然对显示 JDialog 感到困惑。我在this question 中询问过。我认为在收听属性更改时,我仍然需要使用SwingUtilities.invokeLater(或类似的,否则setVisible 似乎会阻止对进度条的更新)。我担心的是代码在SwingUtilities.invokeLater 实际到达setVisible(true) 之前执行setVisible(false) 的可能性(是否存在这种情况?)这意味着对话框不会关闭。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 2020-12-16
    • 2014-08-08
    • 1970-01-01
    相关资源
    最近更新 更多