OS X 上 Java 中的 GUI/线程不稳定性;似乎与秋千/AWT->可可桥有关



我遇到了一个非常令人沮丧的问题,涉及OSX和JavaSwingGUI;特别是我在Cocoa兼容模式方面遇到了问题。

我在一家机器人实验室工作,我们的很多软件都是通过内部模拟套件运行的。这个模拟套件的代码已有近10年的历史,是由我们的一位首席研究人员在大学时创建的。模拟在Swing GUI中运行。模拟在Linux和Windows计算机上运行良好,但在OS X上运行不好。

需要明确的是,我正在编写的代码不是GUI代码。我不是"GUI人"。然而,他很久以前编写的代码提供了一个API,用于快速创建模拟,以基于关节和连杆的标准机械系统对机器人进行建模,这些模拟显示在GUI中,以方便启动、停止、记录、回放、跟踪变量、创建绘图等操作。因此,我不会直接调用任何Swing代码;这都是我们每一个模拟都使用的"沼泽标准"(针对我们的情况)东西。每一个都在OS X上崩溃,而在Windows和Linux下运行得很好。

我在互联网上四处寻找,虽然我还没能找到解决问题的方法,但问题的根源似乎是Cocoa Compatibility Mode希望程序员遵守图形界面的严格线程准则。最容易弹出的一个例子是,如果您不使用SwingUtilities.invokeLater()线程化GUI,那么您的代码将不稳定。编写代码的人承认,当他最初创建API时,他没有遵循尽可能好的线程实践。

Cocoa兼容性模式似乎对某种Swing操作强制执行某种超时,不太确定它是什么,但我使用的是2011款MacBook Pro,它有2.3 GHz四核i7、8 GB RAM、SSD和1 GB Radeon 6750M。我想我不会出现某种暂停。

我们在Eclipse中进行开发,并从Eclipse中启动模拟程序(我们不需要将它们转换为任何形式的可执行程序)。启动模拟时,GUI将出现并完全填充。但是,它不能与之交互。即使是"红绿灯"也永远不会激活,所以杀死GUI的唯一方法就是在Eclipse中使用terminate按钮。

下面是控制台的屏幕截图。我不是Cocoa开发人员,所以错误本身对我来说意义不大,但我知道NS前缀意味着错误来自Cocoa,我知道似乎发生的是某种线程管理器(可能是Grand Central Dispatch?)试图对空锁对象执行操作,而在某个时候线程池出现了问题。

据我所知,目前没有办法禁用Cocoa兼容性模式(过去似乎是通过使用苹果特定的java系统属性来禁用的,但该属性不再出现在苹果开发者网站上提供的有效系统属性列表中)。我还尝试使用-XstartOnFirstThread参数,但没有成功。虽然我们的实验室主要是Linux和Windows驱动的,但我们的组织规模很大,另一个小组希望开始使用我们为他们的项目创建的一些模拟,而这个部门主要是使用Mac的部门(我自己也是OS X用户,所以我想继续使用MBP)。

有人能告诉我是否有办法禁用Cocoa兼容模式吗?或者至少如果我的怀疑是正确的:是什么导致了错误?我们不是官僚机构,我们的老板知道代码有错误,所以如果我的直觉是正确的,那么像在大公司那样进行直觉和重写并不是不可能的。正如我已经说过的,模拟在其他操作系统上没有稳定性问题,许多源于操作系统X限制的稳定性问题似乎与提供愉快的用户体验有关,而我们并不真正关心这一点,因为这不是一款面向用户的产品。它根本不是一个产品。

我不是在寻找关于UX/UIX、非阻塞UI或线程/UI设计最佳实践的评论或答案。这些都是我熟悉的事情,也是我老板非常熟悉的事情。我只希望有人能帮助我消除OS X对Java代码的任意限制,或者有人能为我指明正确的方向,这样我们就可以使当前代码符合OS的限制。

我使用的是适用于OS X的最新JVM/JDK、最新的OS X开发工具和最新版本的Snow Leopard。

编辑:这是一个Gist,它是自包含的并可编译的。它完全没有响应,就像上面一样,虽然它没有所有的空锁错误,但它确实有自动释放错误,在OS X上不起作用。

我遇到了同样的问题(在相同的macbook配置上),并在这里找到了错误描述。

首先,我在OSX:上安装了openjdk 7

  • OS/X Snow and Lion的OpenJDK 7和8
  • 在Mac OS X Lion上安装Java 7和JRuby

我在eclipse中更改了项目构建路径,指向新安装的jdk.1.7.0u(在我的Mac上:/Library/Java/JavaVirtualMachines/1.7.0u.jdk/Contents/Home/),并且不再冻结UI。

除了缺少一个大括号外,您的示例和下面的变体在使用Java 1.6.0_24的Mac OS 10.5.8上按预期运行。特别是,鼠标、键盘和焦点看起来都很正常。注意,JFrameadd()setLayout()(以及其他)转发到内容窗格。我只是不习惯替换内容窗格。

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class FrameExample2 extends JFrame {
private JPanel panel = new JPanel();
private JTextField xAccelTextField = new JTextField(10);
private JSlider xAccelSlider = new JSlider();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
FrameExample2 frame = new FrameExample2();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
});
}
public FrameExample2() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setLayout(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createLineBorder(Color.gray, 8));
panel.add(new JLabel("Linear Acceleration along X", JLabel.CENTER));
panel.add(xAccelSlider);
xAccelTextField.setEditable(false);
xAccelTextField.setText("0");
panel.add(xAccelTextField);
xAccelTextField.setColumns(10);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
xAccelSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
xAccelTextField.setText(
String.valueOf(xAccelSlider.getValue()));
}
});
}
}

最新更新