2013-02-02 95 views
10

創建使用JNI Swing的窗口我使用JNI調用這反過來又創造一個Swing的JFrame,並顯示一個靜態的Java方法。代碼非常簡單,Java代碼獨立工作(即java StartAWT做它應該做的事情),而當使用JNI從C調用時,該過程掛起。Java的JNI:由C

我使用的是Mac OS X 10.8山獅的JDK 1.7.0_09。

這是C代碼,我使用的是調用靜態方法:

JavaVM* jvm; 
JNIEnv* env = create_vm(&jvm); 

jclass class = (*env)->FindClass(env, "StartAWT"); 
jmethodID method = (*env)->GetStaticMethodID(env, class, "run", "()V"); 

(*env)->CallStaticVoidMethod(env, class, method); 

(*jvm)->DestroyJavaVM(jvm); 

StartAWT類看起來是這樣的:

public class StartAWT { 

    public static class Starter implements Runnable { 
     public void run() { 
      System.out.println("Runnning on AWT Queue."); 

      JFrame.setDefaultLookAndFeelDecorated(true); 
      JFrame frame = new JFrame("That's a frame!"); 
      JLabel label = new JLabel("A Label"); 
      frame.getContentPane().add(label); 

      frame.pack(); 
      frame.setVisible(true); 
     } 
    } 

    public static class GUI implements Runnable { 
     public void run() { 
      try { 
       System.out.println("Going to put something on the AWT queue."); 
       SwingUtilities.invokeAndWait(new Starter()); 
      } catch (Exception exc) { 
       throw new RuntimeException(exc); 
      } 
     } 
    } 

    public static void run() { 
     Thread gui = new Thread(new GUI()); 
     gui.start(); 
    } 
} 

當我啓動應用程序,我的確看到Going to put something on the AWT queue但不是Running on AWT Queue

我相信,我的C進程中的虛擬機不具有AWT事件隊列,但我不知道如何設置它爲有一個是(我也沒有肯定,這是什麼原因)。

什麼是爲了展示使用JNI的AWT基於圖形用戶界面來完成?

-

編輯:我插入循環,看看哪個線程是活的,未(可在this gist看到)。在這個版本中,我在另一個線程中調用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 was loaded on first thread -- can't start AWT. (
    0 liblwawt.dylib      0x0000000117e87ad0 JNI_OnLoad + 468 
    1 libjava.dylib      0x00000001026076f1  Java_java_lang_ClassLoader_00024NativeLibrary_load + 207 
    2 ???         0x000000010265af90 0x0 + 4335185808 
) 

這也是我在其他問題已經讀到這裏在計算器上,比如一個在意見建議下面。但是,我找不到解決原始問題的方法。也許值得注意的是,在上面的消息出現之後,主線程仍然存在,即進程死鎖和崩潰都沒有。

-

編輯:我測試的代碼在Linux上它工作正常。所以我認爲這是Cocoa AWT的Mac OS X問題,但我不知道如何繞過它。

-

編輯:我也嘗試了JVM的整個調用移動到一個新的本地線程。這適用於蘋果Java 32位(1.6.0_37)的Mac OS X 10.6,但會導致與上述相同的死鎖。在Mac OS X 10.8上,情況更糟糕,應用程序使用唯一消息「Trace/BPT陷阱:5」(其中seems to be related to loading dynamic libraries)激化。

我也嘗試打包二進制文件,如in this Q&A所述,但發佈失敗,消息爲lsopenurlswithrole() failed with the message -10810,根據Apples Launch Services Reference,這是一個未知錯誤。後者也會發生,而不會嘗試使用AWT(僅僅是JVM調用失敗)。

+0

另請參閱此[問與答](http://stackoverflow.com/q/8750690/230513)。 – trashgod

+0

謝謝,我已經檢查過這個問答,並且在我的應用程序中玩過。但它畢竟不起作用(如另一個問題,也沒有解決方案)。 – scravy

+0

我得到了類似的結果;我只是使用'JavaApplicationStub',但我不知道它是如何工作的。我想知道'JVM TI',引用[這裏](http://stackoverflow.com/a/14492574/230513),有什麼相關的。 – trashgod

回答

7

最後我找到了一個解決方案。

問題不在於創建虛擬機的線程,問題在於AWT事件隊列正在被初始化的線程。換句話說:第一次加載AWT類時,它可能不會加載到主線程中。因此,第1步:在另一個線程上加載(例如)java.awt.Component

但是現在EventQueue會阻塞,因爲它將工作委託給未運行的Cocoa Main Event Queue--因爲它只會在主線程上運行,而主線程是我的應用程序。因此,主運行的循環需要在主線程上啓動:

void 
runCocoaMain() 
{ 
    void* clazz = objc_getClass("NSApplication"); 
    void* app = objc_msgSend(clazz, sel_registerName("sharedApplication")); 

    objc_msgSend(app, sel_registerName("run")); 
} 

我有我的應用程序與Cocoa框架鏈接和包括<objc/objc-runtime.h>。調用runCocoaMain之後主線程被阻塞(因爲事件循環在那裏運行),所以需要爲應用程序本身求助另一個線程。

使用上面的代碼片段運行EventQueue後,其他線程上的AWT類的加載將成功,您可以繼續。

+0

+1以進行持久性。 – trashgod

+0

謝謝!它的作用像魅力。 :) –

+0

不完全理解。你能告訴我「在另一個線程上加載AWT類」嗎? –