Monday, June 26, 2017

Can multiple showMessageDialogs break swing?

Leave a Comment

This is simplified code, which would be called from pressing a button in my main JFrame class. Using this code, and then dismissing one of the resulting dialogs, causes all of my active windows in my Java session to either lock up or just go away.

//Broken code private void buttonActionPerformed(java.awt.event.ActionEvent evt) {     List<JFrame> frameList = new ArrayList<>();     frameList.add(new TestJFrame());     frameList.add(new TestJFrame());      frameList.forEach(frame -> frame.setVisible(true));      frameList.forEach(frame -> {         SwingUtilities.invokeLater(() -> {             JOptionPane.showMessageDialog(frame, "Msg", "Title", 0);             frame.setVisible(false);             frame.dispose();         });     }); } 

However, if I were to remove the SwingUtilities.invokeLater() section then it works like I would expect (dialog pops up, close the dialog, window goes away, repeat).

//Working code private void buttonActionPerformed(java.awt.event.ActionEvent evt) {     List<JFrame> frameList = new ArrayList<>();     frameList.add(new TestJFrame());     frameList.add(new TestJFrame());      frameList.forEach(frame -> frame.setVisible(true));      frameList.forEach(frame -> {         //SwingUtilities.invokeLater(() -> {             JOptionPane.showMessageDialog(frame, "Msg", "Title", 0);             frame.setVisible(false);             frame.dispose();         //});     }); } 

I'd rather not use the second one because the actual code is being started in a background thread that is notifying a set of listeners, so if I were to use the second one then it would block up the thread and slow down listeners until the user responds (when I could be processing during that time). What about the invokeLater() is breaking me? Is this expected behavior?

NOTE: This is simplified code pulled out of how I'm actually using it, but the core issue still exists (I have multiple JFrames, and if multiple JOptionPane.showMessageDialog()s were put on invokeLater()s for different JFrames then it breaks me. I tested the above code with a new, isolated, JFrame class created in Netbeans and was able to reproduce the error.

EDIT: I can't seem to reproduce the error in Windows, only seems to happen in Linux.

3 Answers

Answers 1

It is most likely invokeLater() which is breaking your code. If you want to thread this action try using a simple thread or

  EventQueue.invokeLater(new Runnable()    {             public void run()              { //Your joptionpane here              }      });` 

Answers 2

Instead of invoke later i prefer using a 1) simple thread 2) TimerTask 3) ScheduledExecutorService

Use one of these methods. This is an example for using timer task

import java.util.Timer; import java.util.TimerTask; public class Task2 {    public static void main(String[] args) {      TimerTask task = new TimerTask() {       @Override       public void run() {         // task to run goes here         System.out.println("Hello !!!");       }     };     Timer timer = new Timer();     long delay = 0;     long intevalPeriod = 1 * 1000;      // schedules the task to be run in an interval      timer.scheduleAtFixedRate(task, delay,                             intevalPeriod);   } // end of main } 

If you are not satisfied you can use invoke later. But remember never use invoke and wait its a bad idea

Answers 3

Here is my approach, as I understand the problem is on the locked windows, which are waiting for an event to finish, in swing the events are related with the AWT-EventQueue.

Here is a little explanation about: https://stackoverflow.com/a/22534931/1670134

So, in order to get your window working I used the Future type:

From the java doc:

A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.

package com.stackoverflow.future;  import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit;  import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.SwingWorker;  import com.stackoverflow.frame.MyFrame;  public class MySwingWorker extends SwingWorker<Void, Void>{      @Override     protected Void doInBackground() throws Exception {         final ExecutorService service = Executors.newFixedThreadPool(1);         List<Future<JFrame>> frameList = new ArrayList<Future<JFrame>>();         frameList.add(service.submit(new SwingLoader(new MyFrame())));         frameList.add(service.submit(new SwingLoader(new MyFrame())));          try {             service.shutdown();             service.awaitTermination(5, TimeUnit.SECONDS);         } catch (InterruptedException ex) {             ex.printStackTrace();         } finally {             service.shutdownNow();         }         return null;     }      public static void main(String[] args) {         MySwingWorker mySwingWorker = new MySwingWorker();         SwingUtilities.invokeLater(mySwingWorker);     }  } 

The loader:

package com.stackoverflow.future;  import java.util.concurrent.Callable;  import javax.swing.JFrame;  public class SwingLoader implements Callable<JFrame>{      private JFrame frame;      public SwingLoader(JFrame frame){         this.frame = frame;     }      @Override     public JFrame call() throws Exception {         frame.setVisible(true);         return frame;     }  } 

NOTE: This code is just a proto, in order to provide you with ideas and it must be modified and cleaned in order to achieve the desired results.

Here you are a link with a couple of explanations of each type: http://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment