JAR Bundler使用OSXAdapter导致应用程序滞后或终止

我创build了一个简单的Java应用程序,每秒钟连续10秒钟向JTable添加一行。 它由三个类组成。

程序启动后被调用的主类

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

一个创buildGUI并通过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开发,我需要能够将我的应用程序与特定的文件types(比如.jarbundlerproblem.jarbundlerproblem ,所以我必须使用Apple Jar Bundler将我的JAR文件捆绑到一个APP 。 我已经成功完成了这个任务,我的应用程序打开,数到十,写出每一秒。

现在,为了这个问题

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

由于我需要能够看到什么文件被双击,我正在使用OSXAdapter ,这是一个由苹果为此目的的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和Jar Bundler的文档。 我究竟做错了什么? 或者不应该在第一时间使用OSXAdapter或Jar Bundler?

做完之后,我并不完全相信SwingWorker是一个更简单的(更好的)解决scheme – 仍然需要额外的线程同步(在工作线程和传入文件/名称的“外部”线程之间)。 无论如何(趁机学习,并以错误为准),下面是基本思想的概念示例的粗略certificate:

  • 将控制器实现为SwingWorker,SwingWorker将来自外部线程的input汇集到EDT中
  • 使其通过doWork(..)接受input(来自适配器,fi),将input排队等待发布
  • 实现doInBackground来连续发布input

开放式问题

  • 同步对本地列表的访问(不是并发的专家,但非常确定需要完成)
  • 可靠地检测到外部线程的结束(这里简单地在input队列为空时停止)

反馈欢迎:-)

 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()); } 

它看起来像阻塞事件调度线程 (EDT)。 SwingWorker将是一个更好的select,但是这个例子实现了Runnable

附录:您可以看看这个项目的MVC架构的例子。 它还展示了如何在不使用JAR Bundler的情况下构buildMac OS应用程序包。 更多关于MVC的信息可以在这里find。

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

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

附录:您可以调用isDispatchThread()来检查。 引用的项目包括一个Mac应用程序的.dmg文件和一个ant文件,通过目标dist2 在原地创build该文件包。

附录:另请参阅此处显示的替代方法。

在这里输入图像描述

 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(); } }); } } 

下面是@ kleopatra的一个变例 ,其中持续运行的Controller接受doWork()新条目,而SwingWorker在其后台线程中asynchronous处理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(); } } 
Interesting Posts