3

以下は、実際の問題コードのコンパイル済みプログラムのレプリカです。

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class Dummy {

    public static boolean getUserCheck(int size, boolean Check) {
        if (Check) {
            int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
                    "Warning", 0);
            if (ret > 0) {
                System.out.println("User said No: " + ret);
                return false;
            } else if (ret <= 0) {
                System.out.println("user said Yes: " + ret);
                return true;
            }
        }
        return true;
    }

    public static void workerMethod1() {
        System.out.println("am worker method 1");
    }

    public static void workerMethod2() {
        System.out.println("am worker method 2");
    }

    public static void main(String[] args) {
        System.out.println("mainthread code line 1");
        int size = 13;
        boolean thresholdBreach = true;

        if (getUserCheck(size, thresholdBreach)) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    workerMethod1();
                }
            });

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    workerMethod2();
                }
            });
        }
        System.out.println("mainthread code line 2");
        System.out.println("mainthread code line 3");
    }
}

if{}別のスレッドで main() のブロックを実行したい場所。この2行なので、

        System.out.println("mainthread code line 2");
        System.out.println("mainthread code line 3");

if(){}ブロックの完了を待つ必要はありません

もう 1 つの問題は、専門家がイベント スレッドで確認ダイアログ メソッドを実行することを推奨していることです。

int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
                    "Warning", 0);

私を助けてください!!!!

4

2 に答える 2

3

JOptionPane は Swing メソッドであり、EDT、Event D ispatch Thread およびこのスレッドでのみ呼び出す必要があるため、上記のすべてのコードは EDT で実行する必要があり、呼び出しのほとんどは完全に不要。必要なのは、Swing GUI コードを起動するメインの場所と、バックグラウンド スレッド内から Swing 呼び出しを行う必要があるすべての領域だけです。繰り返しますが、上記のコードのいずれかがバックグラウンド スレッド内で作成されている場合、JOptionPane はそのスレッド内にあってはなりません。SwingUtilities.invokeLater(new Runnable()

この回答または他の回答のより具体的な情報については、質問でより具体的な情報を提供してください。すべての混乱を終わらせましょう。あなたの問題を完全かつ迅速に理解してもらう最善の方法は、最小限のサンプル プログラムを作成して投稿することです。変更せずにコンパイルして実行します。

私は、MVC ラインに沿った適切なリファクタリングが問題のほとんどを解決できるのではないかとこっそり疑っています。あなたのコードは、相互に続く必要があるコード行と if ブロックで非常に直線的であり、GUI とも密接に結合されています。私にとっては 2 つの危険信号です。おそらく、線形コードを減らし、イベントと状態駆動型のコードを増やし、バックグラウンド コードがオブザーバー通知を介して GUI とやり取りし、バックグラウンド コードが同様にコントロール通知から GUI の状態変化に応答するコードの方がよいでしょう。


コントロールには 2 つの SwingWorker が必要です。1 つは行数を取得するため、もう 1 つはユーザーがそうすることに決めた場合に残りのデータを取得するためです。最初の SwingWorker に PropertyChangeListener を追加して、行数データの準備ができたときに通知を受け取り、準備が整ったらビューに表示して、ユーザーが続行するかどうかを選択できるようにします。彼が続行することを決定した場合、私は 2 番目の SwingWorker を呼び出してデータの本体を取得します。


たとえば、私が話していることの大まかなスケッチは次のとおりです。

import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

@SuppressWarnings("serial")
public class SwingWorkerFooView extends JPanel {
   private static final int PREF_W = 400;
   private static final int PREF_H = 300;
   private JProgressBar progressBar;
   private JDialog dialog;

   public SwingWorkerFooView() {
      add(new JButton(new ButtonAction("Foo", this)));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   public boolean showOptionGetAllData(int numberOfRows) {
      String message = "Number of rows = " + numberOfRows + ". Get all of the data?";
      String title = "Get All Of Data?";
      int optionType = JOptionPane.YES_NO_OPTION;
      int result = JOptionPane.showConfirmDialog(this, message, title, optionType);

      return result == JOptionPane.YES_OPTION;
   }

   public void showProgressBarDialog() {
      progressBar = new JProgressBar();
      progressBar.setIndeterminate(true);
      Window window = SwingUtilities.getWindowAncestor(this);
      dialog = new JDialog(window, "Hang on", ModalityType.APPLICATION_MODAL);
      JPanel panel = new JPanel();
      panel.add(progressBar);
      dialog.add(panel);
      dialog.pack();
      dialog.setLocationRelativeTo(this);
      dialog.setVisible(true);
   }

   public void closeProgressBarDialog() {
      dialog.dispose();
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("SwingWorkerFoo");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new SwingWorkerFooView());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

@SuppressWarnings("serial")
class ButtonAction extends AbstractAction {
   Workers workers = new Workers();
   private SwingWorker<Integer, Void> firstWorker;
   private SwingWorker<List<String>, Void> secondWorker;
   private SwingWorkerFooView mainGui;

   public ButtonAction(String name, SwingWorkerFooView mainGui) {
      super(name);
      this.mainGui = mainGui;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      firstWorker = workers.createFirstWorker();
      firstWorker.addPropertyChangeListener(new FirstPropertyChangeListener());
      firstWorker.execute();
      mainGui.showProgressBarDialog();
   }

   private class FirstPropertyChangeListener implements PropertyChangeListener {

      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            mainGui.closeProgressBarDialog();
            try {
               int numberOfRows = firstWorker.get();
               boolean getAllData = mainGui.showOptionGetAllData(numberOfRows);
               if (getAllData) {
                  secondWorker = workers.createSecondWorker();
                  secondWorker.addPropertyChangeListener(new SecondPropertyChangeListener());
                  secondWorker.execute();
                  mainGui.showProgressBarDialog();
               } else {
                  // user decided not to get all data
                  workers.cleanUp();
               }
            } catch (InterruptedException e) {
               e.printStackTrace();
            } catch (ExecutionException e) {
               e.printStackTrace();
            }
         }
      }
   }

   private class SecondPropertyChangeListener implements PropertyChangeListener {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            mainGui.closeProgressBarDialog();
            try {
               List<String> finalData = secondWorker.get();

               // display finalData in the GUI
            } catch (InterruptedException e) {
               e.printStackTrace();
            } catch (ExecutionException e) {
               e.printStackTrace();
            }
         }

      }
   }

}

class Workers {
   // database object that may be shared by two SwingWorkers
   private Object someDataBaseVariable;
   private Random random = new Random(); // just for simulation purposes

   private class FirstWorker extends SwingWorker<Integer, Void> {
      @Override
      protected Integer doInBackground() throws Exception {

         // The Thread.sleep(...) is not going to be in final production code
         // it's just to simulate a long running task
         Thread.sleep(4000);

         // here we create our database object and check how many rows there are
         int rows = random.nextInt(10 + 10); // this is just for demonstration purposes only

         // here we create any objects that must be shared by both SwingWorkers
         // and they will be saved in a field of Workers
         someDataBaseVariable = "Fubar";

         return rows;
      }
   }

   private class SecondWorker extends SwingWorker<List<String>, Void> {
      @Override
      protected List<String> doInBackground() throws Exception {

         // The Thread.sleep(...) is not going to be in final production code
         // it's just to simulate a long running task
         Thread.sleep(4000);

         List<String> myList = new ArrayList<>();
         // here we go through the database filling the myList collection

         return myList;
      }
   }

   public SwingWorker<Integer, Void> createFirstWorker() {
      return new FirstWorker();
   }

   public void cleanUp() {
      // TODO clean up any resources and database stuff that will not be used.
   }

   public SwingWorker<List<String>, Void> createSecondWorker() {
      return new SecondWorker();
   }
}

これらすべての鍵は、直線的なコンソール プログラムの方法で考えるのではなく、オブザーバー デザイン パターンを使用することです。つまり、ある種のリスナーを使用して、GUI とモデルの両方の状態の変化をチェックすることです。

それは本質的に次のとおりです。

  • ワーカーを作成する
  • ワーカーにオブザーバーを追加 (プロパティ変更リスナー)
  • ワーカーを実行する
  • 進行状況バー ダイアログを表示するか、なんらかの方法でワーカーが実行中であることをユーザーに通知します。

  • ワーカーの処理が完了すると、リスナーに通知が送信されます。その後、ワーカーに(ここではget()メソッド呼び出しを介して)最終結果を問い合わせることができます。

  • その後、進行状況ダイアログを閉じることができます
  • また、ビューは結果を表示したり、ユーザーから追加情報を取得したりできます。
于 2014-12-23T05:01:26.397 に答える
0

はい; SwingUtilities.invokeLater() は、後で処理するためにランナブルを AWT イベント キューに配置するだけで、いつでも安全に実行できます。

于 2014-12-23T04:25:46.363 に答える