我创建了一个简单的 Java 应用程序,连续 10 秒的每一秒都会向 JTable 添加一个新行。它由三个类组成。
程序启动后调用的主类

public class JarBundlerProblem {
    public static void main(String[] args)
    {
        System.err.println("Initializing controller");
        new Controller();
    }
}
创建 GUI 并通过 doWork() 进行更改的 Controller
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();
    }
}
但是在这个(小)修改之后,我的应用程序开始起作用。有的时候打不开,虽然我在Console里可以看到它刚刚启动(写的是Initializing controller),但是经过几次尝试,它最终还是会启动,但是前10秒窗口会完全空白,之后,将添加 10 行。
帮助
现在,我为此苦苦挣扎,似乎没有很多关于 OSXAdapter 和 Jar Bundler 的文档。我究竟做错了什么?或者我不应该首先使用 OSXAdapter 或 Jar Bundler?

最佳答案

完成之后,我并不完全相信 SwingWorker 是一个更简单(又名:更好)的解决方案 - 仍然需要额外的线程同步(在工作线程和传入文件/名称的“外部”线程之间)。无论如何(借此机会学习,即使是错误:),下面是基本思想的概念示例的粗略证明:

  • 将 Controller 实现为 SwingWorker,它将来自外线程的输入汇集到 EDT 中
  • 使其通过 doWork(..) 方法接受输入(来自适配器,f.i.),该方法将输入排队以发布
  • 实现 doInBackground 来连续发布输入

  • 开放式问题
  • 同步对本地列表的访问(不是并发方面的专家,但很确定需要这样做)
  • 可靠地检测外线程的结束(这里只是在输入队列为空时停止)

  • 欢迎反馈:-)
    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());
    }
    

    10-07 12:24
    查看更多