使用 OSXAdapter 的 JAR Bundler 导致应用程序滞后或终止
我创建了一个简单的 Java 应用程序,在连续 10 秒的时间内,每秒都会向 JTable
添加一个新行。它由三个类组成。
程序启动后调用的主类
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
),因此我必须捆绑我的应用程序将 JAR
文件转换为APP
使用 苹果罐捆绑器。我已经成功完成了此操作,我的应用程序打开,数到十,每秒写出。
现在,对于问题
默认情况下,双击 .jarbundlerproblem
并将该文件与我的应用程序关联,不会将我双击的文件作为参数传递给应用程序。显然,这只是 Java 在 OS X 上的工作。
由于我需要能够查看双击的文件,因此我使用 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();
}
}
但是经过此(较小的)修改后,我的应用程序开始运行。有时,它无法打开,尽管我可以在控制台中看到它刚刚启动(写入了初始化控制器
),但经过几次尝试后,它最终会启动,但窗口会前 10 秒完全空白,之后将添加 10 行。
现在帮助
,我在这个问题上遇到了很大的困难,而且似乎没有很多关于 OSXAdapter 和 Jar Bundler 的文档。我做错了什么?或者我不应该首先使用 OSXAdapter 或 Jar Bundler ?
I've created a simple Java application that each second for for 10 seconds consecutive seconds adds a new row to a JTable
. It consists of three classes.
The main class that gets called once the program is started
public class JarBundlerProblem {
public static void main(String[] args)
{
System.err.println("Initializing controller");
new Controller();
}
}
A controller that creates the GUI and alters it through 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();
}
}
}
}
And finally, the 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});
}
}
Since I'm developing for OS X, and I need to be able to associate my application with a certain file type (let's say .jarbundlerproblem
), I have to bundle my JAR
file into an APP
using Apple Jar Bundler. I have done this successfully, my application opens, counts to ten, writing out each second.
Now, for the problem
By default, double-clicking a .jarbundlerproblem
, and associating the file with my application, will not pass the file I double-clicked as an argument to the application. Apparently, this is just Java on OS X works.
Since I need to be able to see what file was double-clicked, I'm using OSXAdapter which is a Java library made by Apple for the purpose. This, I've implemented by altering the constructor of my Controller
class and added another method 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();
}
}
But after this (minor) modification, my application starts acting up. Sometimes, it doesn't open, even though I can see in the Console that it just started (Initializing controller
is written), but after a few attempts, it will eventually start, but the windows will be completely blank for the first 10 seconds, and after that, the 10 rows will be added.
Help
Now, I've struggled with this quite a bit, and it seems like there isn't a lot of documentation regarding neither OSXAdapter nor Jar Bundler. What am I doing wrong? Or shouldn't I be using OSXAdapter or Jar Bundler in the first place?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
看起来您正在阻止事件调度线程(美东时间)。
SwingWorker
会是更好的选择,但此示例实现了
Runnable
。附录:您可以查看此项目,以获取MVC 架构。它还展示了如何在不使用 JAR Bundler 的情况下构建 Mac OS 应用程序包。有关 MVC 的更多信息可以在此处找到。
顺便说一句,此示例展示了一种自动滚动
JTable
的方法。单击拇指可暂停滚动;释放以恢复。附录:您的应用程序在启动时滞后 10 秒。由于这是
Controller
休眠的确切时间,因此它肯定在 EDT 上休眠。 sscce 将具有决定性作用。相反,在另一个线程上完成工作并在 EDT 上更新模型。SwingWorker
有一个process()
方法可以自动执行此操作,或者您可以使用invokeLater()
,如下所示。在您的应用程序正确同步之前,让 Apple 事件正常工作的希望很小。附录:您可以调用 <在
Controller
中检查 code>isDispatchThread() 。引用的项目包括一个带有 Mac 应用程序的.dmg
和一个通过目标dist2 在原位构建捆绑包的
ant
文件代码>.附录:另请参阅此处显示的替代方法。
It looks like you're blocking the event dispatch thread(EDT).
SwingWorker
would be a better choice, but this example implementsRunnable
.Addendum: You might look at this project for an example of MVC architecture. It also shows how to construct a Mac OS application bundle without using JAR Bundler. More on MVC may be found here.
As an aside, this example shows one approach to auto-scrolling a
JTable
. Click on the thumb to suspend scrolling; release to resume.Addendum: Your application lags for 10 seconds on startup. As this is the exact time for which the
Controller
sleeps, it's surely sleeping on the EDT. An sscce would be dispositive. Instead, do the work on another thread and update the model on the EDT.SwingWorker
has aprocess()
method that does so automatically, or you can useinvokeLater()
as shown below. Until your application is correctly synchronized, there's little hope of getting Apple events to work.Addendum: You can invoke
isDispatchThread()
in theController
to check. The project cited includes a.dmg
with a Mac application and anant
file that builds the bundle in situ via targetdist2
.Addendum: See also the alternate approaches shown here.
完成此操作后,我并不完全相信 SwingWorker 是一个更简单(又名:更好)的解决方案 - 仍然需要额外的线程同步(在工作线程和传递文件/名称的“外部”线程之间)。无论如何(抓住学习的机会,并且是错误的:),下面是基本思想的一个粗略的概念证明示例:
开放问题
欢迎反馈:-)
After doing it, I'm not fully convinced a SwingWorker is a simpler (aka: better) solution - still requires additional thread synching (between the worker thread and the "outer" thread which passes in the file/names). Anyway (taking the opportunity to learn, and be it by errors :), below is a crude proof of concept example for the basic idea:
open issues
Feedback welcome :-)
这是 @kleopatra 的示例的变体< /a> 其中连续运行的
Controller
接受doWork()
中的新条目,而SwingWorker
在其后台线程中异步处理pending
条目。ArrayBlockingQueue
处理同步。Here's a variation of @kleopatra's example in which a continuously running
Controller
accepts new entries indoWork()
, while aSwingWorker
processes thepending
entries asynchronously in its background thread.ArrayBlockingQueue
handles the synchronization.