2016-07-05 2 views
1

У меня есть графический интерфейс с компонентами java.swing, ActionListeners и SwingWorker для выполнения дополнительного кода в отдельном потоке. Я понимаю, что SwingWorker может быть создан только один раз и не может быть завершен, но отменен. Далее я считаю хорошей проверкой статуса SwingWorker с его методом isCancelled() и в случае выхода из метода doInBackground() и соответствующим образом реагировать на метод done(). Это отлично работает, если у вас есть, например, цикл внутри метода doInBackground() и может тестировать isCancelled() на каждой итерации.Отменить длинную задачу в javax.swing framework

Но как вы можете разбить или завершить длинную задачу, выполняемую в методе doInBackground(), например, чтение большого csv (> 1GB) или вызов метода интенсивного процесса из другого класса? Чтобы проиллюстрировать мой вопрос, я построил простую программу, которая показывает мою проблему при выборе большого ввода csv. Кнопка остановки работает нормально с контуром счетчика, но не прерывает импорт csv.

Как я могу нарушить или прекратить длительный процесс? Если это невозможно с SwingWorker, как я могу сделать это с помощью потоков? Будет ли thread.interrupt() быть возможностью? Как бы реализовать это в моем примере?

import java.awt.BorderLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.io.File; 
import java.io.FileReader; 
import java.io.IOException; 
import java.net.URISyntaxException; 
import java.util.concurrent.TimeUnit; 
import javax.swing.JButton; 
import javax.swing.JFileChooser; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 
import com.opencsv.CSVReader; 

public class MinimalSwing extends JFrame { 
// fields 
private JButton fileButton, startButton, stopButton; 
private JLabel displayLabel; 
private File csvIn; 
private SwingWorkerClass swingWorker; 

// constructor 
public MinimalSwing() { 
    // set GUI-window properties 
    setSize(300, 200); 
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    setLocation(200, 200); 
    setTitle("MinimalSwing"); 
    setLayout(new BorderLayout(9, 9)); 
    setResizable(false); 

    // set components 
    fileButton = new JButton("Choose File"); 
    fileButton.addActionListener(new ButtonActionListener()); 
    getContentPane().add("North", fileButton); 
    startButton = new JButton("Start"); 
    startButton.setEnabled(false); 
    startButton.addActionListener(new ButtonActionListener()); 
    getContentPane().add("West", startButton); 
    stopButton = new JButton("Stop"); 
    stopButton.setEnabled(false); 
    stopButton.addActionListener(new ButtonActionListener()); 
    getContentPane().add("East", stopButton); 
    displayLabel = new JLabel("Status..."); 
    getContentPane().add("South", displayLabel); 
} 

// csvFileChooser for import 
private File getCsv() { 
    JFileChooser fc = new JFileChooser(); 
    int openDialogReturnVal = fc.showOpenDialog(null); 
    if(openDialogReturnVal != JFileChooser.APPROVE_OPTION){ 
     System.out.println("ERROR: Invalid file choice."); 
    } 
    return fc.getSelectedFile(); 
} 

// csvImporter 
private class CsvImporter { 
    public void readCsv(File file) throws IOException { 
     CSVReader reader = new CSVReader(new FileReader(file)); 
     String [] nextLine; 
     reader.readNext(); 
     while ((nextLine = reader.readNext()) != null) {  
      displayLabel.setText("..still reading"); 
     } 
     reader.close(); 
     displayLabel.setText("..actually done."); 
    } 
} 

// ActionListener 
private class ButtonActionListener implements ActionListener { 
    @Override 
    public void actionPerformed(ActionEvent e) { 
     if(e.getSource() == fileButton) { 
      csvIn = getCsv(); 
      if(csvIn != null) { 
       startButton.setEnabled(true); 
       stopButton.setEnabled(true); 
      } 
     } 
     else if(e.getSource() == startButton) { 
      fileButton.setEnabled(false); 
      startButton.setEnabled(false); 
      stopButton.setEnabled(true); 
      swingWorker = new SwingWorkerClass(); 
      swingWorker.execute(); 
     } 
     else { 
      fileButton.setEnabled(true); 
      startButton.setEnabled(true); 
      stopButton.setEnabled(false); 
      swingWorker.cancel(true); 
     } 
    } 
} 

// swingWorker to interact with further program 
private class SwingWorkerClass extends SwingWorker<Boolean, Void> { 
    @Override 
    protected Boolean doInBackground() throws Exception { 
     long t0 = System.currentTimeMillis(); 
     displayLabel.setText("starting execution..."); 
     displayLabel.setText("..importing csv"); 
     CsvImporter csvImporter = new CsvImporter(); 
     csvImporter.readCsv(csvIn); 
     if(isCancelled()) return false; // this cancels after the import, but I want to cancel during the import... 
     long t1 = System.currentTimeMillis(); 
     displayLabel.setText("csv imported in " + String.format("%,d", t1 - t0) + " ms"); 
     for(short i=1; i<=10; i++) { 
      if(isCancelled()) return false; // this works fine as it is called every second 
      TimeUnit.SECONDS.sleep(1); 
      displayLabel.setText("counter: " + i); 
     } 
     return true; 
    } 

    @Override 
    public void done() { 
     fileButton.setEnabled(true); 
     startButton.setEnabled(true); 
     stopButton.setEnabled(false); 
     if(isCancelled()) { 
      displayLabel.setText("Execution cancelled."); 
     } 
     else { 
      displayLabel.setText("Execution succeeded."); 
     } 
    } 
} 

// main 
public static void main(String[] args) throws URISyntaxException, IOException { 
    // launch gui 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      try { 
       MinimalSwing frame = new MinimalSwing(); 
       frame.setVisible(true); 
      } 
      catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    }); 
} 

}

+0

[для ....] (http://stackoverflow.com/a/6114890/714968) [пример ...] (http://stackoverflow.com/questions/7053865/cant- get-arrayindexoutofboundsexception-from-future-and-swingworker-if-threa) – mKorbel

ответ

1

Вы можете сделать свой CSVImporter расширить SwingWorker вместо того, чтобы еще один класс SwingWorkerClass. Таким образом, вы можете получить больше контроля и отменить задачу импорта.

Что-то вроде ниже.

private class CsvImporter extends SwingWorker<Boolean, Void> { 

     public boolean readCsv(File file) throws IOException { 
      CSVReader reader = new CSVReader(new FileReader(file)); 
      String[] nextLine; 
      reader.readNext(); 
      while ((nextLine = reader.readNext()) != null) { 
       displayLabel.setText("..still reading"); 
       if (isCancelled()) 
        return false; // this cancels after the import, but I want 
            // to cancel during the import... 
      } 
      reader.close(); 
      displayLabel.setText("..actually done."); 
      return true; // read complete 
     } 

     @Override 
     protected Boolean doInBackground() throws Exception { 
      long t0 = System.currentTimeMillis(); 
      displayLabel.setText("starting execution..."); 
      displayLabel.setText("..importing csv"); 
      CsvImporter csvImporter = new CsvImporter(); 
      boolean readStatus = csvImporter.readCsv(csvIn); 
      if (readStatus) { 
       long t1 = System.currentTimeMillis(); 
       displayLabel.setText("csv imported in " + String.format("%,d", t1 - t0) + " ms"); 
       for (short i = 1; i <= 10; i++) { 
        if (isCancelled()) 
         return false; // this works fine as it is called every second 
        TimeUnit.SECONDS.sleep(1); 
        displayLabel.setText("counter: " + i); 
       } 
      } 
      return readStatus; 
     } 
    } 
+0

Спасибо, Бенитон! Поэтому, чтобы расширить свой ответ на более общее правило: лучше ли для каждой долговременной задачи обертывать его в класс, который расширяет SwingWorker? Помимо чтения большого CSV, я могу захотеть сделать с ним несколько интенсивных вычислений, которые я ввел в другой класс с помощью нескольких методов. Извините, я довольно новичок в java ... – Newton

+0

@ DanielMeyer Да, это хорошая практика для разработки Long running tasks для продления Worker. или инкапсулировать его. Чтобы вы получили больше контроля над своим работником. – Beniton

Смежные вопросы