2013-02-02 3 views
10

J'utilise JNI pour appeler une méthode java statique qui à son tour crée une JFrame Swing et l'affiche. Le code est assez simple et le code Java fonctionne de manière autonome (c'est-à-dire que java StartAWT fait ce qu'il doit) alors que lorsqu'il est appelé depuis C en utilisant JNI, le processus se bloque. J'utilise le JDK 1.7.0_09 sur Mac OS X 10.8 Mountain Lion.Java JNI: Création d'une fenêtre Swing à l'aide de JNI à partir de C

C'est le code C J'utilise pour appeler la méthode statique:

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); 

La classe StartAWT ressemble à ceci:

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(); 
    } 
} 

Lorsque je démarre l'application, je ne vois Going to put something on the AWT queue mais pas Running on AWT Queue. Je crois que la machine virtuelle dans mon processus C n'a pas de file d'attente d'événements AWT mais je ne sais pas comment la configurer pour en avoir une (et je ne suis pas sûr que ce soit la raison).

Que faire pour afficher une interface graphique AWT utilisant JNI?

-

EDIT: Je ai inséré des boucles pour voir quels fils sont en vie et qui ne sont pas (peut être vu dans this gist). Dans cette version, je fais l'invocation de SwingUtilities.invokeAndWait dans un autre thread. Le résultat: Le thread principal est vivant (C). Le premier thread envoyé par Java (pas le thread principal) est vivant; le fil faisant l'appel invokeAndWait est bloqué (je ne pense pas que invokeAndWait est même revenu), la fonction qui doit être exécutée sur EventQueue n'est même pas entrée.

J'ai aussi essayé d'invoquer SwingUtilities.invokeAndWait directement, ce qui donne le message suivant:

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 
) 

C'est aussi ce que je l'ai lu dans d'autres questions à poser sur StackOverflow, comme celui suggéré dans les commentaires au dessous de. Cependant, je n'ai pas trouvé de solution au problème original. Peut-être vaut-il la peine de noter qu'après que le message ci-dessus est apparu, le thread principal est toujours en vie, c'est-à-dire que le processus ne s'est pas bloqué ni ne s'est écrasé.

-

EDIT: J'ai testé le code sur Linux où il fonctionne comme prévu. Donc, je crois que c'est un problème de Mac OS X avec Cocoa AWT, mais je ne sais pas comment le contourner.

-

EDIT: Je essayé aussi déplacer l'invocation complète de la machine virtuelle Java sur un nouveau thread natif. Cela fonctionne sur Mac OS X 10.6 avec Apple Java 32 bits (1.6.0_37), mais entraîne le même blocage que décrit ci-dessus. Sur Mac OS X 10.8 c'est pire, l'application se bloque avec le seul message "Trace/BPT trap: 5" (qui seems to be related to loading dynamic libraries).

J'ai aussi essayé le binaire comme regroupement décrit in this Q&A, mais le lancement échoue avec le message lsopenurlswithrole() failed with the message -10810, ce qui est une erreur inconnue, selon des pommes Launch Services Reference. Ce dernier se produit également sans essayer d'utiliser AWT (la simple invocation JVM échoue).

+0

Voir aussi cette [Q & A] (http://stackoverflow.com/q/8750690/230513). – trashgod

+0

Merci, j'ai vérifié cette Q & A et joué avec mon application; mais cela ne marche pas après tout (comme dans l'autre question, aucune solution n'est donnée là aussi). – scravy

+0

J'ai obtenu un résultat similaire; J'utilise simplement 'JavaApplicationStub', mais je ne sais pas comment cela fonctionne. Je me demande si 'JVM TI', cité [ici] (http://stackoverflow.com/a/14492574/230513), a quelque chose de pertinent. – trashgod

Répondre

7

Enfin j'ai trouvé une solution. Le problème n'est pas sur quel thread la machine virtuelle est créée, le problème est sur quel thread la file d'attente AWT est initialisée. En d'autres termes: La première fois qu'une classe AWT est chargée, elle ne peut pas être chargée sur le thread principal. Ainsi, étape 1: Charger (par exemple) java.awt.Component sur un autre thread. Mais maintenant EventQueue va bloquer, car il délègue le travail à la file d'attente Cocoa Main Event qui n'est pas en cours d'exécution - bien sûr, car il ne fonctionnera que sur le thread principal et le thread principal est mon application. Ainsi, les principaux besoins boucle d'exécution doit être démarré sur le thread principal:

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

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

je devais lier mon application avec le framework Cocoa et comprennent <objc/objc-runtime.h>. Le thread principal est bloqué après l'appel de runCocoaMain (puisque la boucle d'événement est en cours d'exécution), il est donc nécessaire de recourir à un autre thread pour l'application elle-même. Après l'exécution de EventQueue à l'aide de l'extrait ci-dessus, le chargement de la classe AWT sur l'autre thread aboutit et vous pouvez y poursuivre.

+0

+1 pour persévérance. – trashgod

+0

Merci! Cela fonctionne comme le charme. :) –

+0

Pas entièrement compris. Pourriez-vous s'il vous plaît me dire comment "chargement de la classe AWT sur l'autre thread"? –

Questions connexes