JavaJNI:从C使用JNI创建Swing窗口
我使用JNI调用一个静态java方法,该方法反过来创建一个Swing JFrame并显示它。代码相当简单,Java代码是独立工作的(即,JavaJNI:从C使用JNI创建Swing窗口,java,c,swing,java-native-interface,awt,Java,C,Swing,Java Native Interface,Awt,我使用JNI调用一个静态java方法,该方法反过来创建一个Swing JFrame并显示它。代码相当简单,Java代码是独立工作的(即,Javastartawt做它应该做的),而当使用JNI从C调用时,进程挂起 我正在Mac OS X 10.8 Mountain Lion上使用JDK 1.7.009 这是我用来调用静态方法的C代码: JavaVM*jvm; JNIEnv*env=创建虚拟机(&jvm); jclass类=(*env)->FindClass(env,“StartAWT”); jme
Javastartawt
做它应该做的),而当使用JNI从C调用时,进程挂起
我正在Mac OS X 10.8 Mountain Lion上使用JDK 1.7.009
这是我用来调用静态方法的C代码:
JavaVM*jvm;
JNIEnv*env=创建虚拟机(&jvm);
jclass类=(*env)->FindClass(env,“StartAWT”);
jmethodID方法=(*env)->GetStaticMethodID(env,类,“run”,“V”);
(*env)->CallStaticVoidMethod(env,类,方法);
(*jvm)->破坏JavaVM(jvm);
StartAWT
类如下所示:
公共类StartAWT{
公共静态类启动程序实现Runnable{
公开募捐{
System.out.println(“在AWT队列上运行”);
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame=newjframe(“那是一个帧!”);
JLabel标签=新的JLabel(“A标签”);
frame.getContentPane().add(标签);
frame.pack();
frame.setVisible(true);
}
}
公共静态类GUI实现可运行{
公开募捐{
试一试{
System.out.println(“将把某些东西放到AWT队列上。”);
SwingUtilities.invokeAndWait(newstarter());
}捕获(异常exc){
抛出新的运行时异常(exc);
}
}
}
公共静态无效运行(){
线程gui=新线程(new gui());
gui.start();
}
}
当我启动应用程序时,我确实看到将在AWT队列上放置一些内容
,但不会看到在AWT队列上运行
我相信我的C进程中的虚拟机没有AWT事件队列,但我也不知道如何设置它(我也不确定这是原因)
为了使用JNI显示基于AWT的GUI,应该做些什么
--
编辑:我插入了循环以查看哪些线程处于活动状态,哪些线程处于非活动状态(可以在中看到)。在这个版本中,我在另一个线程中调用SwingUtilities.invokeAndWait
。结果:主线程处于活动状态(C)。Java调度的第一个线程(不是主线程)是活动的;执行调用invokeAndWait
的线程被阻塞(我认为invokeAndWait甚至没有返回),甚至没有输入应该在EventQueue上运行的函数
我还尝试直接调用SwingUtilities.invokeAndWait
,这将给出以下消息:
2013-02-02 13:50:23.629 swing[1883:707]Cocoa AWT:Apple AWT Java VM已加载到第一个线程--无法启动AWT。(
0 liblwawt.dylib 0x0000000117e87ad0 JNI_OnLoad+468
1 libjava.dylib 0x00000001026076f1 Java\u Java\u lang\u类加载器\u 00024 NativeLibrary\u加载+207
2???0x000000010265af90 0x0+4335185808
)
这也是我在StackOverflow的其他问题中读到的,比如下面评论中建议的问题。然而,我找不到解决原来问题的办法。也许值得注意的是,在出现上述消息之后,主线程仍然处于活动状态,即进程既没有死锁也没有崩溃
--
EDIT:我在Linux上测试了代码,代码正常工作。所以我相信这是MacOSX与Cocoa AWT之间的问题,但我不知道如何规避它
--
编辑:我还尝试将JVM的整个调用移动到一个新的本机线程上。这可以在Mac OS X 10.6和Apple Java 32位(1.6.0_37)上运行,但会导致与上述相同的死锁。在Mac OS X 10.8上,更糟糕的是,应用程序崩溃时只显示一条消息“Trace/BPT trap:5”(即)
我也试着按照描述绑定二进制文件,但是启动失败,消息是
lsopenurlswithrole(),消息是-10810
,这是一个未知错误,据苹果公司称。后者也会在不尝试使用AWT的情况下发生(仅JVM调用失败)。最后我找到了一个解决方案
问题不是在哪个线程上创建虚拟机,而是在哪个线程上初始化AWT事件队列。换句话说:第一次加载AWT类时,它可能不会加载到主线程上。因此,步骤1:在另一个线程上加载(例如)java.awt.Component
但是现在EventQueue将阻塞,因为它将工作委托给Cocoa主事件队列,而Cocoa主事件队列没有运行——这是肯定的,因为它将只在主线程上运行,而主线程是我的应用程序。因此,主运行循环需要在主线程上启动:
void
runCocoaMain()
{
void* clazz = objc_getClass("NSApplication");
void* app = objc_msgSend(clazz, sel_registerName("sharedApplication"));
objc_msgSend(app, sel_registerName("run"));
}
我必须将我的应用程序与Cocoa框架链接,并包含
。主线程在调用runCocoaMain后被阻塞(因为事件循环正在那里运行),因此需要为应用程序本身求助于另一个线程
使用上述代码段运行EventQueue后,AWT类在另一个线程上的加载将成功,您可以继续加载。我通过的说明解决了类似的问题,即在另一个线程中启动JVM后启动
CFRunLoopRun()
。请参见此。谢谢,我已经检查了Q&A和我的申请;但它毕竟不起作用(正如在另一个问题中,也没有给出解决方案);我只使用了JavaApplicationStub
,但我不知道它是如何工作的。我想知道引用的JVM TI是否有任何相关内容。它在Linux(Ubuntu12.04 LTS 32位)上的工作原理与预期完全一致。万岁!我找到了一个解决方案,请看我的回答:我不完全理解。你能帮我吗