Java SwingWorker с JDialog, показывающим JProgressBar во время сетевой операции JDBC

У меня есть кадр с кнопкой, при нажатии на которую отображается 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);
            }
        });

    }
}

person user1713059    schedule 22.08.2013    source источник


Ответы (1)


Единственное, что я мог бы сделать по-другому, это не использовать SwingUtilities.invokeLater в методе doInBackground для отображения диалогового окна, но, возможно, использовать PropertyChangeListener для отслеживания изменений в рабочем свойстве state.

Я бы также использовал PropertyChangeListener для отслеживания изменений в свойстве progress рабочего. Вместо использования publish для обозначения изменений в прогрессии я бы использовал метод setProgressgetProgress в PropertyChangeListener)

Например... поток Java swingworker для обновления основного графического интерфейса

Я мог бы также подумать о создании пользовательского интерфейса на JPanel и добавить его в JDialog, а не расширять каталог из JDialog, поскольку это дало бы возможность повторно использовать панель другими способами, если вы хотите...

person MadProgrammer    schedule 22.08.2013
comment
разве нет get(), иначе все исключения растворятся в тумане - person mKorbel; 22.08.2013
comment
@mKorbel OP вызывает get в методе done, где реализация SwingWorker - person MadProgrammer; 22.08.2013
comment
Я думаю (причина моего вопроса) не смешивать возвраты с возможным/ожидающим исключением, пожалуйста, не атакуйте, не намеренно, просто у меня есть несколько ошибок против использования SwingWorker по другой причине, это тоже возможно, потому что это примерно теневая трубка, ты ставишь что-то с одной стороны и ждешь (типа как идиот :-) с другой стороны какой-то ожидаемый результат, или я ошибаюсь... - person mKorbel; 22.08.2013
comment
@mKorbel Я тоже не большой поклонник SwingWorker, он имеет место быть. Да, get — это метод блокировки, он будет ждать завершения doInBackground, но поскольку OP использует get в методе done, он немедленно вернется, так как результат доступен. Это поддерживается Callable и Future или кем-то еще :P - person MadProgrammer; 22.08.2013
comment
Спасибо за Ваш интерес. Я все еще озадачен показом JDialog. Я спросил об этом в этом вопросе. Я думаю, что при прослушивании изменений свойств мне все равно нужно использовать SwingUtilities.invokeLater (или что-то подобное, иначе setVisible, похоже, блокирует обновления индикатора выполнения). Меня беспокоит возможность (есть ли такая?) кода, выполняющего setVisible(false) до того, как SwingUtilities.invokeLater действительно удастся добраться до setVisible(true), что означает, что диалоговое окно не будет закрыто. - person user1713059; 22.08.2013
comment
Ваш диалог модальный? - person MadProgrammer; 22.08.2013