2011-09-22 141 views
3

我创建了一个简单的Java应用程序,每秒钟连续10秒钟向JTable添加一个新行。它由三个类组成。JAR Bundler使用OSXAdapter导致应用程序滞后或终止

一旦程序启动

public class JarBundlerProblem { 
    public static void main(String[] args) 
    { 
     System.err.println("Initializing controller"); 
     new Controller(); 
    } 
} 

创建GUI和改变它通过doWork()

public class Controller { 
    public Controller() 
    { 
     doWork(null); 
    } 
    public static void doWork(String s) 
    { 
     GUI gui = new GUI(); 

     for (int i=0; i<10; i++) 
     { 
      gui.addRow("Line "+(i+1)); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

最后,GUI控制器,该控制器被称为主类

import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 

public class GUI { 
    private JFrame frame = new JFrame(); 
    private DefaultTableModel model = new DefaultTableModel(); 
    private JTable table = new JTable(model); 
    private JScrollPane pane = new JScrollPane(table); 

    public GUI() 
    { 
     model.addColumn("Name"); 

     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(pane); 
     frame.pack(); 
     frame.setVisible(true); 
    } 
    public void addRow(String name) 
    { 
     model.addRow(new Object[]{name}); 
    } 
} 

由于我开发OS X的,我需要能够到我的应用程序与特定文件类型关联(比方说.jarbundlerproblem),我一直在使用Apple Jar Bundler捆绑我JAR文件到APP。我已经成功完成了这项工作,我的应用程序打开,数到十,每秒写出。现在

,对这个问题

默认情况下,双击.jarbundlerproblem,并且该文件与我的应用程序相关联,将无法通过我双击作为参数传递给应用程序的文件。显然,这只是OS X上的Java作品。

由于我需要能够看到双击的文件,我正在使用OSXAdapter这是Apple为此目的制作的Java库。这一点,我已经通过改变我Controller类的构造函数来实现,加上另一个方法registerForMacOSXEvents()

public Controller() 
{ 
    registerForMacOSXEvents(); 
    //doWork(null); 
} 
public void registerForMacOSXEvents() { 
    try { 
     OSXAdapter.setFileHandler(this, getClass().getDeclaredMethod("doWork", new Class[] { String.class })); 
    } catch (Exception e) { 
     System.err.println("Error while loading the OSXAdapter:"); 
     e.printStackTrace(); 
    } 
} 

但这(未成年人)的修改之后,我的应用程序开始演戏了。有时,即使在控制台中可以看到它刚刚启动(即写入了Initializing controller),但它仍然不会打开,但经过几次尝试后,它最终会启动,但前10秒窗口将完全空白,之后,将添加10行。

帮助

现在,我已经竭尽全力来应付这一相当多,而且好像还没有关于既不OSXAdapter也不罐捆扎机大量的文档资料。我究竟做错了什么?或者我不应该在第一时间使用OSXAdapter或Jar Bundler?

+0

只是为了确保我了解OSXAdapter(不熟悉MAC):基本上这是一个线程运行 - 你的控制范围之外 - 这与字符串短信中的doWork报告其结果,直到它准备好(文件名?) ,注册后立即开始?它如何表示准备好了? – kleopatra

+0

很高兴帮助:-)请你出示你的解决方案(没有挂单)? – kleopatra

+0

@kleopatra我不知道OSXAdapter是如何工作的,我只是简单地熟悉它。如果您有兴趣了解更多信息,请访问http://developer.apple.com/library/mac/#samplecode/OSXAdapter/Listings/src_OSXAdapter_java。HTML#// apple_ref/DOC/UID/DTS10000685-src_OSXAdapter_java-DontLinkElementID_5 – kba

回答

4

这件事以后,我并不完全相信一个SwingWorker的是一个简单的(又名:更好的)解决方案 - 仍需要额外的线程synching(工作线程和“外”螺纹之间通过文件/名称)。反正(抽空学习,并通过错误:)是它的机会,下面是概念例子的基本思想粗证明:

  • 实现控制器的SwingWorker,其中漏斗输入从外螺纹成在EDT
  • 使其接受通过方法的doWork(..)哪个队列输入用于发布
  • 实现doInBackground到succesively发布输入

开放问题

输入(从适配器,网络)
  • 同步接入到本地列表(没有并发的专家,但可以肯定的是需要做的)
  • 可靠地检测外螺纹的端部(这里简单地停止在输入队列为空)

反馈欢迎:-)

public class GUI { 
    private JFrame frame = new JFrame(); 
    private DefaultTableModel model = new DefaultTableModel(); 
    private JTable table = new JTable(model); 
    private JScrollPane pane = new JScrollPane(table); 

    public GUI() { 
     model.addColumn("Name"); 

     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(pane); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public void addRow(String name) { 
     model.addRow(new Object[] { name }); 
    } 

    /** 
    * Controller is a SwingWorker. 
    */ 
    public static class Controller extends SwingWorker<Void, String> { 
     private GUI gui; 

     private List<String> pending; 

     public Controller() { 
      gui = new GUI(); 
     } 

     public void doWork(String newLine) { 
      if (pending == null) { 
       pending = new ArrayList<String>(); 
       pending.add(newLine); 
       execute(); 
      } else { 
       pending.add(newLine); 
      } 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      while (pending.size() > 0) { 
       publish(pending.remove(0)); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
      return null; 
     } 

     /** 
     * @inherited <p> 
     */ 
     @Override 
     protected void process(List<String> chunks) { 
      for (String object : chunks) { 
       gui.addRow(object); 
      } 
     } 

    } 

    /** 
    * Simulating the adapter. 
    * 
    * Obviously, the real-thingy wouldn't have a reference 
    * to the controller, but message the doWork refectively 
    */ 
    public static class Adapter implements Runnable { 

     Controller controller; 

     public Adapter(Controller controller) { 
      this.controller = controller; 
     } 

     @Override 
     public void run() { 
      for (int i=0; i<10; i++) 
      { 
       controller.doWork("Line "+(i+1)); 
       try { 
        Thread.sleep(100); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

    } 
    public static void main(String[] args) 
    { 
     System.err.println("Initializing controller"); 
     new Adapter(new Controller()).run(); 
    } 

    @SuppressWarnings("unused") 
    private static final Logger LOG = Logger.getLogger(GUI.class.getName()); 
} 
+0

+1重新开放的问题:在'doWork()'上使用'synchronized'应该足够了,'SwingWorker'的'get()'方法将一直等到它的后台线程退出。我(http://stackoverflow.com/questions/7519244/jar-bundler-using-osxadapter-causing-application-to-lag-or-terminate/7568947#7568947)附近连续添加[变化]。 – trashgod

+0

谢谢,kleopatra,使用你的例子,我终于设法得到它所有的工作。我做了,但分开了类,并将所有工作都移到了'doInBackground'中,并一起摆脱了“待处理”列表。 – kba

+0

我同意'SwingWorker'是更好的选择;这听起来好像不需要等候队列。 – trashgod

6

它看起来像你阻止event dispatch thread(美国东部时间)。 SwingWorker将是更好的选择,但此示例实现Runnable

附录:您可以查看project这个MVC体系结构的示例。它还展示了如何在不使用JAR Bundler的情况下构建Mac OS应用程序包。有关MVC的更多信息,请参阅here

另外,这个例子展示了一种自动滚动JTable的方法。点击大拇指暂停滚动;释放以恢复。

附录:您的应用程序在启动时滞后10秒。由于这是Controller睡眠的确切时间,所以肯定会睡在EDT上。 sscce将是dispositive。相反,在另一个线程上工作并更新EDT上的模型。 SwingWorker有自动执行的process()方法,或者您可以使用invokeLater(),如下所示。在您的应用程序正确同步之前,Apple的事件无法运行。

附录:您可以调用isDispatchThread()中的Controller进行检查。引用的项目包括一个带有Mac应用程序的.dmg和一个ant文件,该文件通过目标dist2在原地构建了包

附录:另请参阅here所示的替代方法。

enter image description here

import java.awt.BorderLayout; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.AdjustmentEvent; 
import java.awt.event.AdjustmentListener; 
import javax.swing.AbstractAction; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JProgressBar; 
import javax.swing.JScrollBar; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 

/** @seehttps://stackoverflow.com/questions/7519244 */ 
public class TableAddTest extends JPanel implements Runnable { 

    private static final int N_ROWS = 8; 
    private static String[] header = {"ID", "String", "Number", "Boolean"}; 
    private DefaultTableModel dtm = new DefaultTableModel(null, header) { 

     @Override 
     public Class<?> getColumnClass(int col) { 
      return getValueAt(0, col).getClass(); 
     } 
    }; 
    private JTable table = new JTable(dtm); 
    private JScrollPane scrollPane = new JScrollPane(table); 
    private JScrollBar vScroll = scrollPane.getVerticalScrollBar(); 
    private JProgressBar jpb = new JProgressBar(); 
    private int row; 
    private boolean isAutoScroll; 

    public TableAddTest() { 
     this.setLayout(new BorderLayout()); 
     jpb.setIndeterminate(true); 
     this.add(jpb, BorderLayout.NORTH); 
     Dimension d = new Dimension(320, N_ROWS * table.getRowHeight()); 
     table.setPreferredScrollableViewportSize(d); 
     for (int i = 0; i < N_ROWS; i++) { 
      addRow(); 
     } 
     scrollPane.setVerticalScrollBarPolicy(
      JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 
     vScroll.addAdjustmentListener(new AdjustmentListener() { 

      @Override 
      public void adjustmentValueChanged(AdjustmentEvent e) { 
       isAutoScroll = !e.getValueIsAdjusting(); 
      } 
     }); 
     this.add(scrollPane, BorderLayout.CENTER); 
     JPanel panel = new JPanel(); 
     panel.add(new JButton(new AbstractAction("Add Row") { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       addRow(); 
      } 
     })); 
     this.add(panel, BorderLayout.SOUTH); 
    } 

    private void addRow() { 
     char c = (char) ('A' + row++ % 26); 
     dtm.addRow(new Object[]{ 
       Character.valueOf(c), 
       String.valueOf(c) + String.valueOf(row), 
       Integer.valueOf(row), 
       Boolean.valueOf(row % 2 == 0) 
      }); 
    } 

    private void scrollToLast() { 
     if (isAutoScroll) { 
      int last = table.getModel().getRowCount() - 1; 
      Rectangle r = table.getCellRect(last, 0, true); 
      table.scrollRectToVisible(r); 
     } 
    } 

    @Override 
    public void run() { 
     while (true) { 
      EventQueue.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        addRow(); 
       } 
      }); 
      EventQueue.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        scrollToLast(); 
       } 
      }); 
      try { 
       Thread.sleep(1000); // simulate latency 
      } catch (InterruptedException ex) { 
       System.err.println(ex); 
      } 
     } 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       JFrame f = new JFrame(); 
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       TableAddTest nlt = new TableAddTest(); 
       f.add(nlt); 
       f.pack(); 
       f.setLocationRelativeTo(null); 
       f.setVisible(true); 
       new Thread(nlt).start(); 
      } 
     }); 
    } 
} 
+0

我怀疑捆绑只是暴露了潜在的同步问题。 – trashgod

+0

如果'addRow()'没有从GUI类中调用,你能解释一下如何做到这一点吗? – kba

+0

外观极好模拟+1 – mKorbel

2

这里的@克列奥帕特拉的example在连续运行Controller接受doWork()新条目的变化,而SwingWorker在其后台线程中异步处理pending条目。 ArrayBlockingQueue处理同步。

import java.awt.EventQueue; 
import java.util.List; 
import java.util.Random; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.SwingWorker; 
import javax.swing.table.DefaultTableModel; 

public class GUI { 

    private static final Random rnd = new Random(); 
    private JFrame frame = new JFrame(); 
    private DefaultTableModel model = new DefaultTableModel(); 
    private JTable table = new JTable(model); 
    private JScrollPane pane = new JScrollPane(table); 

    public GUI() { 
     model.addColumn("Name"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(pane); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public void addRow(String name) { 
     model.addRow(new Object[]{name}); 
    } 

    /** 
    * Controller is a SwingWorker. 
    */ 
    private static class Controller extends SwingWorker<Void, String> { 

     private static final int MAX = 5; 
     private GUI gui; 
     private BlockingQueue<String> pending = 
      new ArrayBlockingQueue<String>(MAX); 

     public Controller() { 
      EventQueue.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        gui = new GUI(); 
       } 
      }); 
     } 

     private void doWork(String newLine) { 
      try { 
       pending.put(newLine); 
      } catch (InterruptedException e) { 
       e.printStackTrace(System.err); 
      } 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      while (true) { 
       // may block if nothing pending 
       publish(pending.take()); 
       try { 
        Thread.sleep(rnd.nextInt(500)); // simulate latency 
       } catch (InterruptedException e) { 
        e.printStackTrace(System.err); 
       } 
      } 
     } 

     @Override 
     protected void process(List<String> chunks) { 
      for (String object : chunks) { 
       gui.addRow(object); 
      } 
     } 
    } 

    /** 
    * Exercise the Controller. 
    */ 
    private static class Adapter implements Runnable { 

     private Controller controller; 

     private Adapter(Controller controller) { 
      this.controller = controller; 
     } 

     @Override 
     public void run() { 
      controller.execute(); 
      int i = 0; 
      while (true) { 
       // may block if Controller busy 
       controller.doWork("Line " + (++i)); 
       try { 
        Thread.sleep(rnd.nextInt(500)); // simulate latency 
       } catch (InterruptedException e) { 
        e.printStackTrace(System.err); 
       } 
      } 
     } 
    } 

    public static void main(String[] args) { 
     System.out.println("Initializing controller"); 
     // Could run on inital thread via 
     // new Adapter(new Controller()).run(); 
     // but we'll start a new one 
     new Thread(new Adapter(new Controller())).start(); 
    } 
} 
+0

感谢您的回答,但我完全从我的实际项目中删除了“pending”。 :-) – kba

+0

谢谢 - ArrayBlockingQueue是我今天学习的东西:-)和+1在美国东部时间创建用户界面,我的健忘是传奇... – kleopatra

+0

@kleopatra:我总是欢迎你的见解。我很高兴你发现'ArrayBlockingQueue'有趣。我更新了代码以使用接口['BlockingQueue'](http://download.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html),它的实现各种用例。 – trashgod