我正在使用java swing开发一个简单的应用程序,我希望在Windows,MacOS和Linux中使用它。 当然,我正试图尽可能地将它与操作系统集成。
对于MacOS,我有这个代码,允许我在全局菜单中设置应用程序名称,并为“关于”按钮设置操作。
我正在使用以下代码:
if(System.getProperty("os.name").toUpperCase().startsWith("MAC")){
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "My app name");
System.setProperty("apple.awt.application.name", "My app name");
//Need for macos global menubar
System.setProperty("apple.laf.useScreenMenuBar", "true");
try{
com.apple.eawt.Application app = com.apple.eawt.Application.getApplication();
app.setDockIconImage(Toolkit.getDefaultToolkit().getImage(MainGUI.class.getResource("images/icon.png")));
app.setAboutHandler(new com.apple.eawt.AboutHandler() {
@Override
public void handleAbout(com.apple.eawt.AppEvent.AboutEvent aboutEvent) {
AboutDialog a = new AboutDialog();
a.setTitle("About");
a.pack();
a.setResizable(false);
centerDialogInScreen(a);
a.setVisible(true);
}
});
} catch (Throwable e){
//This means that the application is not being run on MAC OS.
//Just do nothing and go on...
}
}
当我在非macos JAVA上运行我的应用程序,因为它的JRE没有com.apple.eawt。*类时,JVM应该抛出一个NoDefClassFoundError,我正在捕捉并继续,对吗?
似乎没有这样做,当我启动我的应用程序“.jar”时,我得到以下内容(在Windows上):
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/apple/eawt/AboutHandler
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
at java.lang.Class.privateGetMethodRecursive(Unknown Source)
at java.lang.Class.getMethod0(Unknown Source)
at java.lang.Class.getMethod(Unknown Source)
at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.apple.eawt.AboutHandler
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 7 more
我在这里缺少什么?
答案 0 :(得分:2)
抛出NoClassDefFoundError
的确切时间并不固定,但可能取决于JVM实现细节。在您的情况下,这是课程的验证。在HotSpot中,Verifier不会加载所有引用的类,但显然它试图在你的情况下加载AboutHandler
,这可能是因为你的类有一个实现AboutHandler
的内部类而Verifier想要的检查类型层次结构的一致性,即AboutHandler
是否真的是一个接口。确切的细节并不是那么重要,即使你设法解决它,结果也会很脆弱,可能会在其他版本或替代JVM实现中中断。
如果您想要安全起见,则不得直接引用可能不存在的类。因此,您可以使用Class.forName
,Method.invoke
等反射性地执行整个操作,但这会使任何重要的代码变得繁琐。更简单的解决方案是将整个MacOS特定代码放入自己的类中,比如说MacSetup
。然后,您只需通过Class.forName
加载此类(仅在检查您在MacOS上运行后)才能将其分离。
public class MacSetup implements Runnable {
@Override
public void run() {
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "My app name");
System.setProperty("apple.awt.application.name", "My app name");
//Need for macos global menubar
System.setProperty("apple.laf.useScreenMenuBar", "true");
com.apple.eawt.Application app = com.apple.eawt.Application.getApplication();
app.setDockIconImage(Toolkit.getDefaultToolkit().getImage(MainGUI.class.getResource("images/icon.png")));
app.setAboutHandler(new com.apple.eawt.AboutHandler() {
@Override
public void handleAbout(com.apple.eawt.AppEvent.AboutEvent aboutEvent) {
AboutDialog a = new AboutDialog();
a.setTitle("About");
a.pack();
a.setResizable(false);
centerDialogInScreen(a);
a.setVisible(true);
}
});
}
}
主要课程:
if(System.getProperty("os.name").toUpperCase().startsWith("MAC")) {
try {
Class.forName("MacSetup").asSubclass(Runnable.class).newInstance().run();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
// since this code is only executed when we *are* on MacOS, we should report it
Logger.getLogger("MacSetup").log(Level.SEVERE, null, ex);
}
}
因此,主类与MacSetup
类及其所有引用分离,因为它反射性地加载此类并通过始终存在的Runnable
接口调用其实现方法,从而减少了反射操作达到必要的最低限度。
答案 1 :(得分:1)
apple
类不是标准Java类,因此在Mac平台之外不可用。如果您希望您的应用程序是跨平台的,最好的选择是避免使用这些类,即使这意味着与MacOS的集成更少。
另一方面,您可以创建一个特定的'关于'用于不同操作系统的类,并针对特定操作系统进行了扩展(例如MacAbout,WinAbout,....)
确定班级'命名运行时,并使用Class.forName动态加载类,避免JRE需要解析不存在的apple
类。
但是,老实说,我会选择第一个选项并制作一个真正的平台独立应用程序。
答案 2 :(得分:0)
好吧,我设法用反射来解决部分问题。图标部分工作正常,我认为" AboutHandler"如果我能够将代码转换为反射,那么part会工作得很好。 因为它并不重要,我有一些更大的鱼来煎炸我没有太多困扰。 如果有人管理如何使用反射获取AboutHandler,我会将其标记为正确。
if(System.getProperty("os.name").toUpperCase().startsWith("MAC")) {
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "My App Name");
System.setProperty("apple.awt.application.name", "My App Short name");
//Need for macos global menubar
System.setProperty("apple.laf.useScreenMenuBar", "true");
try {
Class application = Class.forName("com.apple.eawt.Application");
Method getApplication = application.getMethod("getApplication");
Object instance = getApplication.invoke(application);
Class[] params = new Class[1];
params[0] = Image.class;
Method setIcon = application.getMethod("setDockIconImage",params);
setIcon.invoke(instance,Toolkit.getDefaultToolkit().getImage(MainGUI.class.getResource("images/icon.png")));
} catch (ClassNotFoundException | NoSuchMethodException |
SecurityException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException exp) {
exp.printStackTrace(System.err);
}
}
答案 3 :(得分:0)
com.apple java包在java9中已弃用并删除(现在默认在MacOS high sierra上)
在新api上查看http://openjdk.java.net/jeps/272以使用
package java.awt;
public class Desktop {
/* ... */
/**
* Adds sub-types of {@link AppEventListener} to listen for notifications
* from the native system.
*
* @param listener
* @see AppForegroundListener
* @see AppHiddenListener
* @see AppReOpenedListener
* @see AppScreenSleepListener
* @see AppSystemSleepListener
* @see AppUserSessionListener
*/
public void addAppEventListener(final AppEventListener listener) {}
/**
* Requests user attention to this application (usually through bouncing the Dock icon).
* Critical requests will continue to bounce the Dock icon until the app is activated.
*
*/
public void requestUserAttention(final boolean critical) {}
/**
* Attaches the contents of the provided PopupMenu to the application's Dock icon.
*/
public void setDockMenu(final PopupMenu menu) {}
/**
* Changes this application's Dock icon to the provided image.
*/
public void setDockIconImage(final Image image) {}
/**
* Affixes a small system provided badge to this application's Dock icon. Usually a number.
*/
public void setDockIconBadge(final String badge) {}
/**
* Displays or hides a progress bar or other indicator in
* the dock.
*
* @see DockProgressState.NORMAL
* @see DockProgressState.PAUSED
* @see DockProgressState.ERROR
*
* @see #setDockProgressValue
*/
public void setDockProgressState(int state) {}
/**
* Sets the progress bar's current value to {@code n}.
*/
public void setDockProgressValue(int n) {}
/**
* Tests whether a feature is supported on the current platform.
*/
public boolean isSupportedFeature(Feature f) {}
/* ... */
}